From: Maximiliano Curia Date: Tue, 28 Jun 2016 17:47:52 +0000 (+0100) Subject: Import kdepim-runtime_16.04.2.orig.tar.xz X-Git-Tag: archive/raspbian/4%18.08.3-6+rpi1^2^2^2~2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=40eaf6fc24db0a9818ea21dab4176899fcc86637;p=kdepim-runtime.git Import kdepim-runtime_16.04.2.orig.tar.xz [dgit import orig kdepim-runtime_16.04.2.orig.tar.xz] --- 40eaf6fc24db0a9818ea21dab4176899fcc86637 diff --git a/.arcconfig b/.arcconfig new file mode 100644 index 00000000..20d53ee2 --- /dev/null +++ b/.arcconfig @@ -0,0 +1,4 @@ +{ + "phabricator.uri" : "https://phabricator.kde.org/project/profile/34/", + "history.immutable" : true +} diff --git a/.emacs-dirvars b/.emacs-dirvars new file mode 100644 index 00000000..d8419e55 --- /dev/null +++ b/.emacs-dirvars @@ -0,0 +1,14 @@ +;; -*- emacs-lisp -*- +;; +;; This file is processed by the dirvars emacs package. Each variable +;; setting below is performed when this dirvars file is loaded. +;; +c-block-comment-prefix: "" +indent-tabs-mode: nil +tab-width: 8 +c-basic-offset: 2 +evaluate: (c-set-offset 'innamespace '0) +evaluate: (c-set-offset 'access-label '0) +;evaluate: (c-set-offset 'topmost-intro '+) +evaluate: (c-set-offset 'inline-open '+) +kdab-qt-version: 4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..cd88be43 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Ignore the following files +*~ +*.[oa] +*.kdev4 +.kdev_include_paths +.swp.* +.*.swp +*.kate-swp +Makefile +*.moc +*.moc.cpp +autom4te.cache +*.diff +svn-commit.tmp +svn-commit.*.tmp +*.kdevelop.pcs +*.kdev4 +svnmerge-commit-message.txt +avail +Doxyfile +*.user +/build/ +CMakeLists.txt.user* + diff --git a/.krazy b/.krazy new file mode 100644 index 00000000..945ea5ad --- /dev/null +++ b/.krazy @@ -0,0 +1,6 @@ +SKIP /libkdepim-copy/ +EXTRA defines,kdebug,null,qenums,tipsandthis +STRICT super + +#kresources is pretty much dead +IGNORESUBS kresources diff --git a/.reviewboardrc b/.reviewboardrc new file mode 100644 index 00000000..4d23bcc7 --- /dev/null +++ b/.reviewboardrc @@ -0,0 +1,5 @@ +REVIEWBOARD_URL = "https://git.reviewboard.kde.org" +TARGET_GROUPS = "kdepim" +REPOSITORY = "kdepim-runtime" +TARGET_PEOPLE = "mlaurent" +BRANCH = "master" diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..9280777b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,154 @@ +cmake_minimum_required(VERSION 2.8.12) + +project(kdepim-runtime) + +############### KDEPIM-Runtime version ################ +# KDEPIM_RUNTIME_VERSION +# Version scheme: "x.y.z build". +# +# x is the version number. +# y is the major release number. +# z is the minor release number. +# +# "x.y.z" follow the kdelibs version kdepim is released with. +# +# If "z" is 0, it the version is "x.y" +# +# KDEPIM_RUNTIME_DEV_VERSION +# is empty for final versions. For development versions "build" is +# something like "pre", "alpha1", "alpha2", "beta1", "beta2", "rc1", "rc2". +# +# Examples in chronological order: +# +# 3.0 +# 3.0.1 +# 3.1 alpha1 +# 3.1 beta1 +# 3.1 beta2 +# 3.1 rc1 +# 3.1 +# 3.1.1 +# 3.2 pre +# 3.2 alpha1 + +if(NOT DEFINED KDEPIM_RUNTIME_DEV_VERSION) + set(KDEPIM_RUNTIME_DEV_VERSION "") +endif() + +set(KDEPIM_RUNTIME_VERSION_NUMBER "5.2.2") +set(KDEPIM_RUNTIME_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}${KDEPIM_RUNTIME_DEV_VERSION}") + +configure_file(kdepim-runtime-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdepim-runtime-version.h @ONLY) + +find_package(ECM 5.19 REQUIRED NO_MODULE) +set(CMAKE_MODULE_PATH ${kdepim-runtime_SOURCE_DIR}/cmake/modules ${ECM_MODULE_PATH}) + +include(ECMPackageConfigHelpers) +include(ECMSetupVersion) +include(FeatureSummary) +include(KDEInstallDirs) +include(KDECMakeSettings) +include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) +include(ECMInstallIcons) +include(ECMQtDeclareLoggingCategory) + +set(KF5_VERSION "5.19") +set(QT_REQUIRED_VERSION "5.5.0") + +set(KDEPIMLIBS_LIB_VERSION "5.2.0") + +set(KDEPIMRUNTIME_LIB_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}") +set(KDEPIMRUNTIME_LIB_SOVERSION "5") +set(AKONADI_VERSION "5.2.0") + +set(KCONTACTS_LIB_VERSION "5.2.0") +set(KCALENDARCORE_LIB_VERSION "5.2.0") +set(IDENTITYMANAGEMENT_LIB_VERSION "5.2.0") +set(KMAILTRANSPORT_LIB_VERSION "5.2.0") +set(CALENDARUTILS_LIB_VERSION "5.2.0") +set(KIMAP_LIB_VERSION "5.2.0") +set(KMBOX_LIB_VERSION "5.2.0") +set(AKONADICALENDAR_LIB_VERSION "5.2.0") +set(SYNDICATION_LIB_VERSION "5.2.0") +set(KONTACTINTERFACE_LIB_VERSION "5.2.0") +set(AKONADIKALARM_LIB_VERSION "5.2.0") +set(KMIME_LIB_VERSION "5.2.0") +set(XMLRPCCLIENT_LIB_VERSION "5.2.0") +set(KCONTACTS_LIB_VERSION "5.2.0") +set(AKONADIMIME_LIB_VERSION "5.2.0") +set(AKONADICONTACT_LIB_VERSION "5.2.0") +set(AKONADINOTE_LIB_VERSION "5.2.0") +set(AKONADISOCIALUTIL_LIB_VERSION "5.2.0") +set(KPIMTEXTEDIT_LIB_VERSION "5.2.0") + +set( SHARED_MIME_INFO_MINIMUM_VERSION "0.40" ) +find_package( SharedMimeInfo REQUIRED ) + + +find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Network Widgets Test XmlPatterns DBus) + +# QT5 package +find_package(Qt5WebKitWidgets ${QT_REQUIRED_VERSION} REQUIRED NO_MODULE) + +find_package(Qt5 OPTIONAL_COMPONENTS TextToSpeech) +if (NOT Qt5TextToSpeech_FOUND) + message(STATUS "Qt5TextToSpeech not found, speech feature will be disabled") +else() + add_definitions(-DHAVE_SPEECH) +endif() + + +# KF5 package +find_package(KF5KDELibs4Support ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5ConfigWidgets ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5NotifyConfig ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5KIO ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5ItemModels ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5Kross ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED) +find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED) +find_packagE(KF5TextWidgets ${KF5_VERSION} CONFIG REQUIRED) # for KPluralHandlingSpinBox + +# KdepimLibs package +find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) +find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5MailTransport ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiContact ${AKONADICONTACT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5Contacts ${KCONTACTS_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AlarmCalendar ${AKONADIKALARM_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5CalendarCore ${KCALENDARCORE_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5CalendarUtils ${CALENDARUTILS_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5Mbox ${KMBOX_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5PimTextEdit ${KPIMTEXTEDIT_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5IMAP ${KIMAP_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5Syndication ${SYNDICATION_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiNotes ${AKONADINOTE_LIB_VERSION} CONFIG REQUIRED) +find_package(KF5AkonadiSocialUtils ${AKONADISOCIALUTIL_LIB_VERSION} CONFIG REQUIRED) + +option(KDEPIM_RUN_ISOLATED_TESTS "Run the isolated tests." FALSE) + +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +add_subdirectory(resources) +add_subdirectory(agents) +add_subdirectory(plugins) +add_subdirectory(defaultsetup) +add_subdirectory(kioslave) +add_subdirectory(migration) + + +## install the MIME type spec file for KDEPIM specific MIME types +install(FILES kdepim-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR}) +update_xdg_mimetypes(${KDE_INSTALL_MIMEDIR}) + + +install( FILES kdepim-runtime.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) + +feature_summary(WHAT ALL + INCLUDE_QUIET_PACKAGES + FATAL_ON_MISSING_REQUIRED_PACKAGES +) diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..5185fd3f --- /dev/null +++ b/COPYING @@ -0,0 +1,346 @@ +NOTE! The GPL below is copyrighted by the Free Software Foundation, but +the instance of code that it refers to (the kde programs) are copyrighted +by the authors who actually wrote it. + +--------------------------------------------------------------------------- + + 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 Library 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) 19yy + + 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) 19yy 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 Library General +Public License instead of this License. diff --git a/COPYING.LIB b/COPYING.LIB new file mode 100644 index 00000000..2d2d780e --- /dev/null +++ b/COPYING.LIB @@ -0,0 +1,510 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + 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 St, 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/CTestConfig.cmake b/CTestConfig.cmake new file mode 100644 index 00000000..e8bc4ba7 --- /dev/null +++ b/CTestConfig.cmake @@ -0,0 +1,13 @@ +## This file should be placed in the root directory of your project. +## Then modify the CMakeLists.txt file in the root directory of your +## project to incorporate the testing dashboard. +## # The following are required to uses Dart and the Cdash dashboard +## ENABLE_TESTING() +## INCLUDE(CTest) +set(CTEST_PROJECT_NAME "kdepim-runtime") +set(CTEST_NIGHTLY_START_TIME "20:00:00 CET") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=kdepim-runtime") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/CTestCustom.cmake b/CTestCustom.cmake new file mode 100644 index 00000000..33852484 --- /dev/null +++ b/CTestCustom.cmake @@ -0,0 +1,31 @@ +# This file contains all the specific settings that will be used +# when running 'make Experimental' + +# Change the maximum warnings that will be displayed +# on the report page (default 50) +set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS 2000) + +# Warnings that will be ignored +set(CTEST_CUSTOM_WARNING_EXCEPTION + + "too big, try a different debug format" + "qlist.h.*increases required alignment of target type" + "qmap.h.*increases required alignment of target type" + "qhash.h.*increases required alignment of target type" + ".*warning.* is deprecated" + ) + +# Errors that will be ignored +set(CTEST_CUSTOM_ERROR_EXCEPTION + + "ICECC" + "Segmentation fault" + "Error 1 (ignored)" + "invoking macro kDebug argument 1" + "GConf Error" + "Client failed to connect to the D-BUS daemon" + "Failed to connect to socket" + ) + +# No coverage for these files +set(CTEST_CUSTOM_COVERAGE_EXCLUDE ".moc$" "moc_" "ui_" "ontologies") diff --git a/MIGRATE-CONFIG-APPS b/MIGRATE-CONFIG-APPS new file mode 100644 index 00000000..805013d1 --- /dev/null +++ b/MIGRATE-CONFIG-APPS @@ -0,0 +1,3 @@ +Migrate configrc done: +- agent/newmailnotifieragent: akonadi_newmailnotifier_agentrc, akonadi_newmailnotifier_agent.notifyrc +- agent/maildispatcher: maildispatcheragentrc, akonadi_maildispatcher_agent.notifyrc diff --git a/Mainpage.dox b/Mainpage.dox new file mode 100644 index 00000000..2b1bd767 --- /dev/null +++ b/Mainpage.dox @@ -0,0 +1,105 @@ +/** +\mainpage Akonadi, the KDE PIM storage framework + +These pages are a combination of design and api documentation. If you are +looking for general information go to the Overview section. +For detailed information and/or api-dox on any of the packages go to the +package main page, either from the menu on the left or from the Building +blocks section below. + + +\section akonadi_overview Overview + +- Design and Concepts + - \ref akonadi_design_communication +- \ref akonadi_usage +- Website +- Wiki +- \ref akonadi_todos + +\section akonadi_building_blocks Building Blocks + +- Domain-specific libraries: + - Contacts (KABC) + - MIME Messages (KMime) + - Events, todo items and journal entries (KCal) + - Core library (libakonadi) + +\authors Tobias König , Volker Krause + +\licenses \lgpl +*/ + + +/** +\page akonadi_design_communication Communication Schemas + +\section akonadi_design_communication_search Search + +The sequence diagrams below show how general communication is done: + +
+ \image html akonadi_client_search_small.png "Akonadi Communication Schema" +
+ + +\image latex akonadi_client_search.eps "Akonadi Communication Schema" height=5cm + +The item search request is probably the call which is used most often +by the clients (components or applications). This call enables the client +to search for a list of items of a given mime type which match a +given search criterion. + +In this case the client will contact the SearchProvider responsible for +the mime type, in order to retrieve the list of matching UIDs. The SearchProvider +already has a list of all available items of this mime type in its memory, so it +can search fast and use indices for optimization. + +To communicate mime type constraints in FETCH and LIST and their responses the +IMAP flags mechanism is used. Unknown flags should be ignored by non-Akonadi +IMAP clients, which keeps compatibility with mutt and regular KMail. + +Examples: +- List +\verbatim +0x8053c68 8 LIST "" "res1/foo/%" +0x8053c68 * LIST (\MimeTypes[text/calendar,directory/inode]) "/" "res1/foo/bar" +\endverbatim +- Fetch +\verbatim +0x8056310 7 UID FETCH 22 (UID RFC822.SIZE FLAGS BODY.PEEK[]) +0x8056310 * 1 FETCH (FLAGS (\Seen \MimeTypes[message/rfc822]) RFC822 {2450} From: Till Adam To: ... +\endverbatim + +\section akonadi_design_communication_agent Agent Handling +
+ \image html akonadi_agent_handling_small.png "Akonadi Agent Handling" +
+ + +\image latex akonadi_agent_handling.eps "Akonadi Agent Handling" height=4cm +*/ + +/** +\page akonadi_overview_uml Akonadi Overview + +This overview does not show a complete class or collaboration diagram, it is rather meant to show the +big picture. + +
+ \image html akonadi_overview_uml_small.png "Akonadi Overview" +
+ + + +*/ + + +// DOXYGEN_NAME=kdepim-runtime +// DOXYGEN_ENABLE=YES diff --git a/README b/README new file mode 100644 index 00000000..646778a3 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +The KDE project kdepim-runtime contains the Akonadi resources from kdepim which can be used without the applications in kdepim. + +Here is the online code-repostitory of kdepim-runtime: http://quickgit.kde.org/?p=kdepim-runtime.git&a=summary diff --git a/agents/.krazy b/agents/.krazy new file mode 100644 index 00000000..4152f9b2 --- /dev/null +++ b/agents/.krazy @@ -0,0 +1 @@ +SKIP /ontologies/ diff --git a/agents/CMakeLists.txt b/agents/CMakeLists.txt new file mode 100644 index 00000000..a4586ce8 --- /dev/null +++ b/agents/CMakeLists.txt @@ -0,0 +1,10 @@ +project(agents) + +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +add_subdirectory( maildispatcher ) +add_subdirectory( newmailnotifier ) +add_subdirectory( migration ) +add_subdirectory( invitations ) + diff --git a/agents/Info.plist.template b/agents/Info.plist.template new file mode 100644 index 00000000..c39ddb95 --- /dev/null +++ b/agents/Info.plist.template @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + LSUIElement + 1 + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + + diff --git a/agents/Mainpage.dox b/agents/Mainpage.dox new file mode 100644 index 00000000..c99c6ac3 --- /dev/null +++ b/agents/Mainpage.dox @@ -0,0 +1,2 @@ +// DOXYGEN_NAME=Akonadi Agents +// DOXYGEN_ENABLE=YES diff --git a/agents/cmake/FindXsltproc.cmake b/agents/cmake/FindXsltproc.cmake new file mode 100644 index 00000000..45b46cfc --- /dev/null +++ b/agents/cmake/FindXsltproc.cmake @@ -0,0 +1,32 @@ +# Find xsltproc executable and provide a macro to generate D-Bus interfaces. +# +# The following variables are defined : +# XSLTPROC_EXECUTABLE - path to the xsltproc executable +# Xsltproc_FOUND - true if the program was found +# +find_program(XSLTPROC_EXECUTABLE xsltproc DOC "Path to the xsltproc executable") +mark_as_advanced(XSLTPROC_EXECUTABLE) + +if(XSLTPROC_EXECUTABLE) + set(Xsltproc_FOUND TRUE) + + # We depend on kdepimlibs, make sure it's found + if(NOT DEFINED KF5Akonadi_DATA_DIR) + find_package(KF5Akonadi REQUIRED) + endif() + + + # Macro to generate a D-Bus interface description from a KConfigXT file + macro(kcfg_generate_dbus_interface _kcfg _name) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + COMMAND ${XSLTPROC_EXECUTABLE} --stringparam interfaceName ${_name} + ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + > ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + DEPENDS ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + ) + endmacro() +endif() + diff --git a/agents/invitations/CMakeLists.txt b/agents/invitations/CMakeLists.txt new file mode 100644 index 00000000..be62d37d --- /dev/null +++ b/agents/invitations/CMakeLists.txt @@ -0,0 +1,28 @@ + + +set( invitationsagent_SRCS + invitationsagent.cpp + incidenceattribute.cpp +) + +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_invitations_agent\") + +add_executable(akonadi_invitations_agent ${invitationsagent_SRCS}) + +target_link_libraries(akonadi_invitations_agent + KF5::AkonadiCore + KF5::AkonadiMime + KF5::Mime + KF5::CalendarCore + KF5::AkonadiAgentBase +) + +if( APPLE ) + set_target_properties(akonadi_invitations_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_invitations_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.invitationsagent") + set_target_properties(akonadi_invitations_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Invitations Calendar") +endif () + + +install(TARGETS akonadi_invitations_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES invitationsagent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents") diff --git a/agents/invitations/Messages.sh b/agents/invitations/Messages.sh new file mode 100755 index 00000000..6f0436ae --- /dev/null +++ b/agents/invitations/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh + +$XGETTEXT *.cpp -o $podir/akonadi_invitations_agent.pot diff --git a/agents/invitations/incidenceattribute.cpp b/agents/invitations/incidenceattribute.cpp new file mode 100644 index 00000000..d3c277e7 --- /dev/null +++ b/agents/invitations/incidenceattribute.cpp @@ -0,0 +1,93 @@ +/* + Copyright (c) 2009 Sebastian Sauer + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "incidenceattribute.h" + +#include +#include + +using namespace Akonadi; + +class IncidenceAttribute::Private +{ +public: + QString status; + Akonadi::Item::Id referenceId; + + explicit Private() : referenceId(-1) {} +}; + +IncidenceAttribute::IncidenceAttribute() + : Attribute(), d(new Private) +{ +} + +IncidenceAttribute::~IncidenceAttribute() +{ + delete d; +} + +QByteArray IncidenceAttribute::type() const +{ + static const QByteArray sType("incidence"); + return sType; +} + +Attribute *IncidenceAttribute::clone() const +{ + IncidenceAttribute *other = new IncidenceAttribute; + return other; +} + +QByteArray IncidenceAttribute::serialized() const +{ + QString data; + QTextStream out(&data); + out << d->status; + out << d->referenceId; + return data.toUtf8(); +} + +void IncidenceAttribute::deserialize(const QByteArray &data) +{ + QString s(QString::fromUtf8(data)); + QTextStream in(&s); + in >> d->status; + in >> d->referenceId; +} + +QString IncidenceAttribute::status() const +{ + return d->status; +} + +void IncidenceAttribute::setStatus(const QString &newstatus) const +{ + d->status = newstatus; +} + +Akonadi::Item::Id IncidenceAttribute::reference() const +{ + return d->referenceId; +} + +void IncidenceAttribute::setReference(Akonadi::Item::Id id) +{ + d->referenceId = id; +} diff --git a/agents/invitations/incidenceattribute.h b/agents/invitations/incidenceattribute.h new file mode 100644 index 00000000..21ccc0de --- /dev/null +++ b/agents/invitations/incidenceattribute.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2009 Sebastian Sauer + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef INCIDENCEATTRIBUTE_H +#define INCIDENCEATTRIBUTE_H + +#include +#include + +namespace Akonadi +{ + +class IncidenceAttribute : public Akonadi::Attribute +{ +public: + explicit IncidenceAttribute(); + ~IncidenceAttribute(); + + QByteArray type() const Q_DECL_OVERRIDE; + Attribute *clone() const Q_DECL_OVERRIDE; + + QByteArray serialized() const Q_DECL_OVERRIDE; + void deserialize(const QByteArray &data) Q_DECL_OVERRIDE; + + /** + * The status the invitation is in. + * + * One of; + * "new", "accepted", "tentative", "counter", "cancel", "reply", "delegated" + */ + QString status() const; + void setStatus(const QString &newstatus) const; + + /** + * The referenced item. This is used e.g. in the invitationagent to + * let users know where the original mail message is. + */ + Akonadi::Item::Id reference() const; + void setReference(Akonadi::Item::Id id); + +private: + class Private; + Private *const d; +}; + +} + +#endif diff --git a/agents/invitations/invitationsagent.cpp b/agents/invitations/invitationsagent.cpp new file mode 100644 index 00000000..9f0d4783 --- /dev/null +++ b/agents/invitations/invitationsagent.cpp @@ -0,0 +1,569 @@ +/* + Copyright 2009 Sebastian Sauer + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "invitationsagent.h" + +#include "incidenceattribute.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Akonadi; + +class InvitationsCollectionRequestJob : public SpecialCollectionsRequestJob +{ +public: + InvitationsCollectionRequestJob(SpecialCollections *collection, InvitationsAgent *agent) + : SpecialCollectionsRequestJob(collection, agent) + { + setDefaultResourceType(QStringLiteral("akonadi_ical_resource")); + + QVariantMap options; + options.insert(QStringLiteral("Path"), QString(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("akonadi_invitations"))); + options.insert(QStringLiteral("Name"), i18n("Invitations")); + setDefaultResourceOptions(options); + + QMap displayNameMap; + displayNameMap.insert("invitations", i18n("Invitations")); + setTypes(displayNameMap.keys()); + setNameForTypeMap(displayNameMap); + + QMap iconNameMap; + iconNameMap.insert("invitations", QStringLiteral("folder")); + setIconForTypeMap(iconNameMap); + } + + virtual ~InvitationsCollectionRequestJob() + { + } +}; + +class InvitationsCollection : public SpecialCollections +{ +public: + + class Settings : public KCoreConfigSkeleton + { + public: + Settings() + { + setCurrentGroup(QStringLiteral("Invitations")); + addItemString(QStringLiteral("DefaultResourceId"), m_id, QString()); + } + + virtual ~Settings() + { + } + + private: + QString m_id; + }; + + InvitationsCollection(InvitationsAgent *agent) + : Akonadi::SpecialCollections(new Settings), m_agent(agent), sInvitationsType("invitations") + { + } + + virtual ~InvitationsCollection() + { + } + + void clear() + { + m_collection = Collection(); + } + + bool hasDefaultCollection() const + { + return SpecialCollections::hasDefaultCollection(sInvitationsType); + } + + Collection defaultCollection() const + { + if (!m_collection.isValid()) { + m_collection = SpecialCollections::defaultCollection(sInvitationsType); + } + + return m_collection; + } + + void registerDefaultCollection() + { + defaultCollection(); + registerCollection(sInvitationsType, m_collection); + } + + SpecialCollectionsRequestJob *reguestJob() const + { + InvitationsCollectionRequestJob *job = new InvitationsCollectionRequestJob(const_cast(this), + m_agent); + job->requestDefaultCollection(sInvitationsType); + + return job; + } + +private: + InvitationsAgent *m_agent; + const QByteArray sInvitationsType; + mutable Collection m_collection; +}; + +InvitationsAgentItem::InvitationsAgentItem(InvitationsAgent *agent, const Item &originalItem) + : QObject(agent), m_agent(agent), m_originalItem(originalItem) +{ +} + +InvitationsAgentItem::~InvitationsAgentItem() +{ +} + +void InvitationsAgentItem::add(const Item &item) +{ + qDebug(); + const Collection collection = m_agent->collection(); + Q_ASSERT(collection.isValid()); + + ItemCreateJob *job = new ItemCreateJob(item, collection, this); + connect(job, &InvitationsCollectionRequestJob::result, this, &InvitationsAgentItem::createItemResult); + + m_jobs << job; + + job->start(); +} + +void InvitationsAgentItem::createItemResult(KJob *job) +{ + ItemCreateJob *createJob = qobject_cast(job); + m_jobs.removeAll(createJob); + if (createJob->error()) { + qWarning() << "Failed to create new Item in invitations collection." << createJob->errorText(); + return; + } + + if (!m_jobs.isEmpty()) { + return; + } + + ItemFetchJob *fetchJob = new ItemFetchJob(m_originalItem, this); + connect(fetchJob, &ItemFetchJob::result, this, &InvitationsAgentItem::fetchItemDone); + fetchJob->start(); +} + +void InvitationsAgentItem::fetchItemDone(KJob *job) +{ + if (job->error()) { + qWarning() << "Failed to fetch Item in invitations collection." << job->errorText(); + return; + } + + ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob->items().count() == 1); + + Item modifiedItem = fetchJob->items().at(0); + Q_ASSERT(modifiedItem.isValid()); + + modifiedItem.setFlag(Akonadi::MessageFlags::HasInvitation); + ItemModifyJob *modifyJob = new ItemModifyJob(modifiedItem, this); + connect(modifyJob, &ItemModifyJob::result, this, &InvitationsAgentItem::modifyItemDone); + modifyJob->start(); +} + +void InvitationsAgentItem::modifyItemDone(KJob *job) +{ + if (job->error()) { + qWarning() << "Failed to modify Item in invitations collection." << job->errorText(); + return; + } + + //ItemModifyJob *mj = qobject_cast( job ); + //qDebug()<<"Job successful done."; +} + +InvitationsAgent::InvitationsAgent(const QString &id) + : AgentBase(id), AgentBase::ObserverV2() + , m_invitationsCollection(new InvitationsCollection(this)) +{ + qDebug(); + + changeRecorder()->setChangeRecordingEnabled(false); // behave like Monitor + changeRecorder()->itemFetchScope().fetchFullPayload(); + changeRecorder()->setMimeTypeMonitored(QStringLiteral("message/rfc822"), true); + //changeRecorder()->setCollectionMonitored( Collection::root(), true ); + + connect(this, &InvitationsAgent::reloadConfiguration, this, &InvitationsAgent::initStart); + QTimer::singleShot(0, this, &InvitationsAgent::initStart); +} + +InvitationsAgent::~InvitationsAgent() +{ + delete m_invitationsCollection; +} + +void InvitationsAgent::initStart() +{ + qDebug(); + + m_invitationsCollection->clear(); + if (m_invitationsCollection->hasDefaultCollection()) { + initDone(); + } else { + SpecialCollectionsRequestJob *job = m_invitationsCollection->reguestJob(); + connect(job, &InvitationsCollectionRequestJob::result, this, &InvitationsAgent::initDone); + job->start(); + } + + /* + KConfig config( "akonadi_invitations_agent" ); + KConfigGroup group = config.group( "General" ); + m_resourceId = group.readEntry( "DefaultCalendarAgent", "default_ical_resource" ); + newAgentCreated = false; + m_invitations = Akonadi::Collection(); + AgentInstance resource = AgentManager::self()->instance( m_resourceId ); + if ( resource.isValid() ) { + Q_EMIT status( AgentBase::Running, i18n( "Reading..." ) ); + QMetaObject::invokeMethod( this, "createAgentResult", Qt::QueuedConnection ); + } else { + Q_EMIT status( AgentBase::Running, i18n( "Creating..." ) ); + AgentType type = AgentManager::self()->type( QLatin1String( "akonadi_ical_resource" ) ); + AgentInstanceCreateJob *job = new AgentInstanceCreateJob( type, this ); + connect(job, &InvitationsCollectionRequestJob::result, this, &InvitationsAgent::createAgentResult); + job->start(); + } + */ +} + +void InvitationsAgent::initDone(KJob *job) +{ + if (job) { + if (job->error()) { + qWarning() << "Failed to request default collection:" << job->errorText(); + return; + } + + m_invitationsCollection->registerDefaultCollection(); + } + + Q_ASSERT(m_invitationsCollection->defaultCollection().isValid()); + Q_EMIT status(AgentBase::Idle, i18n("Ready to dispatch invitations")); +} + +Collection InvitationsAgent::collection() +{ + return m_invitationsCollection->defaultCollection(); +} + +#if 0 +KIdentityManagement::IdentityManager *InvitationsAgent::identityManager() +{ + if (!m_IdentityManager) { + m_IdentityManager = new KIdentityManagement::IdentityManager(true /* readonly */, this); + } + return m_IdentityManager; +} +#endif + +void InvitationsAgent::configure(WId windowId) +{ + qDebug() << windowId; + /* + QWidget *parent = QWidget::find( windowId ); + QDialog *dialog = new QDialog( parent ); + QVBoxLayout *layout = new QVBoxLayout( dialog->mainWidget() ); + //layout->addWidget( ); + dialog->mainWidget()->setLayout( layout ); + */ + initStart(); //reload +} + +#if 0 +void InvitationsAgent::createAgentResult(KJob *job) +{ + qDebug(); + AgentInstance agent; + if (job) { + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to create resource: %1", job->errorString())); + return; + } + + AgentInstanceCreateJob *j = static_cast(job); + agent = j->instance(); + agent.setName(i18n("Invitations")); + m_resourceId = agent.identifier(); + + QDBusInterface conf(QString::fromLatin1("org.freedesktop.Akonadi.Resource.") + m_resourceId, + QString::fromLatin1("/Settings"), + QString::fromLatin1("org.kde.Akonadi.ICal.Settings")); + QDBusReply reply = conf.call(QString::fromLatin1("setPath"), + QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + "akonadi_ical_resource"); + + if (!reply.isValid()) { + qWarning() << "dbus call failed, m_resourceId=" << m_resourceId; + Q_EMIT status(AgentBase::Broken, i18n("Failed to set the directory for invitations via D-Bus")); + AgentManager::self()->removeInstance(agent); + return; + } + + KConfig config("akonadi_invitations_agent"); + KConfigGroup group = config.group("General"); + group.writeEntry("DefaultCalendarAgent", m_resourceId); + + newAgentCreated = true; + agent.reconfigure(); + } else { + agent = AgentManager::self()->instance(m_resourceId); + Q_ASSERT(agent.isValid()); + } + + ResourceSynchronizationJob *j = new ResourceSynchronizationJob(agent, this); + connect(j, &AgentInstanceCreateJob::result, this, &InvitationsAgent::resourceSyncResult); + j->start(); +} + +void InvitationsAgent::resourceSyncResult(KJob *job) +{ + qDebug(); + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to synchronize collection: %1", job->errorString())); + if (newAgentCreated) { + AgentManager::self()->removeInstance(AgentManager::self()->instance(m_resourceId)); + } + return; + } + CollectionFetchJob *fjob = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive, this); + fjob->fetchScope().setContentMimeTypes(QStringList() << "text/calendar"); + fjob->fetchScope().setResource(m_resourceId); + connect(fjob, &CollectionFetchJob::result, this, &InvitationsAgent::collectionFetchResult); + fjob->start(); +} + +void InvitationsAgent::collectionFetchResult(KJob *job) +{ + qDebug(); + + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to fetch collection: %1", job->errorString())); + if (newAgentCreated) { + AgentManager::self()->removeInstance(AgentManager::self()->instance(m_resourceId)); + } + return; + } + + CollectionFetchJob *fj = static_cast(job); + + if (newAgentCreated) { + // if the agent was just created then there is exactly one collection already + // and we just use that one. + Q_ASSERT(fj->collections().count() == 1); + m_invitations = fj->collections().at(0); + initDone(); + return; + } + + KConfig config("akonadi_invitations_agent"); + KConfigGroup group = config.group("General"); + const QString collectionId = group.readEntry("DefaultCalendarCollection", QString()); + if (!collectionId.isEmpty()) { + // look if the collection is still there. It may the case that there exists such + // a collection with the defined collectionId but that this is not a valid one + // and therefore not in the resultset. + const int id = collectionId.toInt(); + foreach (const Collection &c, fj->collections()) { + if (c.id() == id) { + m_invitations = c; + initDone(); + return; + } + } + } + + // we need to create a new collection and use that one... + Collection c; + c.setName("invitations"); + c.setParent(Collection::root()); + Q_ASSERT(!m_resourceId.isNull()); + c.setResource(m_resourceId); + c.setContentMimeTypes(QStringList() + << "text/calendar" + << "application/x-vnd.akonadi.calendar.event" + << "application/x-vnd.akonadi.calendar.todo" + << "application/x-vnd.akonadi.calendar.journal" + << "application/x-vnd.akonadi.calendar.freebusy"); + CollectionCreateJob *cj = new CollectionCreateJob(c, this); + connect(cj, &CollectionCreateJob::result, this, &InvitationsAgent::collectionCreateResult); + cj->start(); +} + +void InvitationsAgent::collectionCreateResult(KJob *job) +{ + qDebug(); + if (job->error()) { + qWarning() << job->errorString(); + Q_EMIT status(AgentBase::Broken, i18n("Failed to create collection: %1", job->errorString())); + if (newAgentCreated) { + AgentManager::self()->removeInstance(AgentManager::self()->instance(m_resourceId)); + } + return; + } + CollectionCreateJob *j = static_cast(job); + m_invitations = j->collection(); + initDone(); +} +#endif + +Item InvitationsAgent::handleContent(const QString &vcal, + const KCalCore::MemoryCalendar::Ptr &calendar, + const Item &item) +{ + KCalCore::ICalFormat format; + KCalCore::ScheduleMessage::Ptr message = format.parseScheduleMessage(calendar, vcal); + if (!message) { + qWarning() << "Invalid invitation:" << vcal; + return Item(); + } + + qDebug() << "id=" << item.id() << "remoteId=" << item.remoteId() << "vcal=" << vcal; + + KCalCore::Incidence::Ptr incidence = message->event().staticCast(); + Q_ASSERT(incidence); + + IncidenceAttribute *attr = new IncidenceAttribute; + attr->setStatus(QStringLiteral("new")); //TODO + //attr->setFrom( message->from()->asUnicodeString() ); + attr->setReference(item.id()); + + Item newItem; + newItem.setMimeType(incidence->mimeType()); + newItem.addAttribute(attr); + newItem.setPayload(KCalCore::Incidence::Ptr(incidence->clone())); + return newItem; +} + +void InvitationsAgent::itemAdded(const Item &item, const Collection &collection) +{ + qDebug() << item.id() << collection; + Q_UNUSED(collection); + + if (!m_invitationsCollection->defaultCollection().isValid()) { + qDebug() << "No default collection found"; + return; + } + + if (collection.isVirtual()) { + return; + } + + if (!item.hasPayload()) { + qDebug() << "Item has no payload"; + return; + } + + KMime::Message::Ptr message = item.payload(); + + //TODO check if we are the sender and need to ignore the message... + //const QString sender = message->sender()->asUnicodeString(); + //if ( identityManager()->thatIsMe( sender ) ) return; + + KCalCore::MemoryCalendar::Ptr calendar(new KCalCore::MemoryCalendar(KSystemTimeZones::local())); + if (message->contentType()->isMultipart()) { + qDebug() << "message is multipart:" << message->attachments().size(); + + InvitationsAgentItem *it = Q_NULLPTR; + foreach (KMime::Content *content, message->contents()) { + + KMime::Headers::ContentType *ct = content->contentType(); + Q_ASSERT(ct); + qDebug() << "Mimetype of the body part is " << ct->mimeType(); + if (ct->mimeType() != "text/calendar") { + continue; + } + + Item newItem = handleContent(QLatin1String(content->body()), calendar, item); + if (!newItem.hasPayload()) { + qDebug() << "new item has no payload"; + continue; + } + + if (!it) { + it = new InvitationsAgentItem(this, item); + } + + it->add(newItem); + } + } else { + qDebug() << "message is not multipart"; + + KMime::Headers::ContentType *ct = message->contentType(); + Q_ASSERT(ct); + qDebug() << "Mimetype of the body is " << ct->mimeType(); + if (ct->mimeType() != "text/calendar") { + return; + } + + qDebug() << "Message has an invitation in the body, processing"; + + Item newItem = handleContent(QLatin1String(message->body()), calendar, item); + if (!newItem.hasPayload()) { + qDebug() << "new item has no payload"; + return; + } + + InvitationsAgentItem *it = new InvitationsAgentItem(this, item); + it->add(newItem); + } +} + +AKONADI_AGENT_MAIN(InvitationsAgent) + diff --git a/agents/invitations/invitationsagent.desktop b/agents/invitations/invitationsagent.desktop new file mode 100644 index 00000000..e5000d30 --- /dev/null +++ b/agents/invitations/invitationsagent.desktop @@ -0,0 +1,97 @@ +[Desktop Entry] +Name=Invitations Dispatcher Agent +Name[bs]=Agent za raspodjelu poziva +Name[ca]=Agent distribuïdor d'invitacions +Name[ca@valencia]=Agent distribuïdor d'invitacions +Name[cs]=Agent odesílatele pozvánek +Name[da]=Invitationsafsendingsagent +Name[de]=Agent zur Einladungs-Auslieferung +Name[el]=Πράκτορας αποστολής προσκλήσεων +Name[en_GB]=Invitations Dispatcher Agent +Name[es]=Agente despachador de invitaciones +Name[et]=Kutsete edastamise agent +Name[fi]=Kutsunlähetysagentti +Name[fr]=Agent de diffusions d'invitations +Name[gl]=Axente de Despacho de Convites +Name[hu]=Meghívófeladó ügynök +Name[ia]=Agente Distributor de Invitationes +Name[it]=Agente per la consegna degli inviti +Name[ja]=正体ディスパッチャーエージェント +Name[kk]=Шақыру реттеуш агенті +Name[km]=ការ​អញ្ជើញ​​ភ្នាក់ងារ​​កម្មវិធី​បញ្ជូន +Name[ko]=초대장 가져오기 에이전트 +Name[lt]=Laiškų gijų išdėstymo agentas +Name[lv]=Ielūgumu nosūtīšanas aģents +Name[nb]=Agent for invitasjonssendinger +Name[nds]=Inladenverdeel-Hölper +Name[nl]=Uitnodigingen-verspreidingsagent +Name[pl]=Agent wysyłania zaproszeń +Name[pt]=Agente de Despacho de Convites +Name[pt_BR]=Agente de encaminhamento de convites +Name[ro]=Agent de remitere a invitațiilor +Name[ru]=Агент диспетчера приглашений +Name[sk]=Agent spracovania pozvánok +Name[sl]=Posrednik za razpošiljanje povabil +Name[sr]=Агент отпремања позивница +Name[sr@ijekavian]=Агент отпремања позивница +Name[sr@ijekavianlatin]=Agent otpremanja pozivnica +Name[sr@latin]=Agent otpremanja pozivnica +Name[sv]=Modul för inbjudningssändning +Name[tr]=Davetiye Dağıtıcı Programı +Name[uk]=Агент розподілу запрошень +Name[x-test]=xxInvitations Dispatcher Agentxx +Name[zh_CN]=邀请签发代理 +Name[zh_TW]=邀請配送代理程式 +Comment=Dispatches invitations from your calendar +Comment[bs]=Raspoređuje pozive iz vašeg kalendara +Comment[ca]=Distribueix invitacions des del calendari +Comment[ca@valencia]=Distribueix invitacions des del calendari +Comment[da]=Udsender invitationer fra din kalender +Comment[de]=Verschickt Einladungen aus Ihren Kalender +Comment[el]=Διανέμει προσκλήσεις από το ημερολόγιό σας +Comment[en_GB]=Dispatches invitations from your calendar +Comment[es]=Remite invitaciones desde su calendario +Comment[et]=Kutsete edastamine sinu kalendrist +Comment[fi]=Lähettää kutsuja kalenteristasi +Comment[fr]=Diffuse les invitations à partir de votre agenda +Comment[gl]=Xestiona invitacións do calendario. +Comment[hu]=Meghívásokat kézbesít a naptárából +Comment[ia]=Expedi invitationes ex tu calendario +Comment[it]=Consegna inviti dal tuo calendario +Comment[kk]=Күнтізбеңіздегі шақыруларды үлестіру +Comment[ko]=달력에서 초대장을 가져옴 +Comment[lt]=Išsiunčia pakvietimus iš Jūsų kalendoriaus +Comment[nb]=Sender ut invitasjoner fra din kalender +Comment[nds]=Verdeelt Inladen ut Dien Kalenner +Comment[nl]=Brengt uitnodigingen uit uw agenda naar elders +Comment[pl]=Rozsyła zaproszenia z twojego kalendarza +Comment[pt]=Trata dos convites do seu calendário +Comment[pt_BR]=Encaminhamento de convites do seu calendário +Comment[ro]=Remite invitații din calendar +Comment[ru]=Рассылает приглашения из календаря +Comment[sk]=Vybavuje pozvánky z vášho kalendára +Comment[sl]=Odpošlje povabila z vašega koledarja +Comment[sr]=Отпрема позивнице из вашег календара +Comment[sr@ijekavian]=Отпрема позивнице из вашег календара +Comment[sr@ijekavianlatin]=Otprema pozivnice iz vašeg kalendara +Comment[sr@latin]=Otprema pozivnice iz vašeg kalendara +Comment[sv]=Skickar inbjudningar från kalendern +Comment[tr]=Takviminizdeki davetleri yollar +Comment[uk]=Роповсюджує запрошення на основі даних календаря +Comment[x-test]=xxDispatches invitations from your calendarxx +Comment[zh_CN]=从您的日历发送邀请 +Comment[zh_TW]=從您的行事曆中分配邀請 +Type=AkonadiAgent +Exec=akonadi_invitations_agent +Icon=mail-folder-outbox + +X-Akonadi-Identifier=akonadi_invitations_agent +#X-Akonadi-Capabilities=Unique,Autostart,NoConfig +X-Akonadi-Capabilities=NoConfig + +#X-Akonadi-MimeTypes=message/rfc822 +#X-Akonadi-MimeTypes=text/calendar +X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.event +#X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.todo +#X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.journal +#X-Akonadi-MimeTypes=application/x-vnd.akonadi.calendar.freebusy diff --git a/agents/invitations/invitationsagent.h b/agents/invitations/invitationsagent.h new file mode 100644 index 00000000..4d35c50d --- /dev/null +++ b/agents/invitations/invitationsagent.h @@ -0,0 +1,101 @@ +/* + Copyright 2009 Sebastian Sauer + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef INVITATIONSAGENT_H +#define INVITATIONSAGENT_H + +#include + +#include +#include +#include +#include + +#include + +class KJob; + +class InvitationsAgent; +class InvitationsCollection; + +class InvitationsAgentItem : public QObject +{ + Q_OBJECT + +public: + InvitationsAgentItem(InvitationsAgent *a, const Akonadi::Item &originalItem); + virtual ~InvitationsAgentItem(); + void add(const Akonadi::Item &newItem); + +private Q_SLOTS: + void createItemResult(KJob *job); + void fetchItemDone(KJob *); + void modifyItemDone(KJob *job); + +private: + InvitationsAgent *m_agent; + const Akonadi::Item m_originalItem; + QList m_jobs; +}; + +class InvitationsAgent : public Akonadi::AgentBase, public Akonadi::AgentBase::ObserverV2 +{ + Q_OBJECT + +public: + explicit InvitationsAgent(const QString &id); + virtual ~InvitationsAgent(); + + Akonadi::Collection collection(); + +public Q_SLOTS: + void configure(WId windowId) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void initStart(); + void initDone(KJob *job = Q_NULLPTR); + +private: + Akonadi::Item handleContent(const QString &vcal, + const KCalCore::MemoryCalendar::Ptr &calendar, + const Akonadi::Item &item); + + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) Q_DECL_OVERRIDE; + + /* + virtual void itemChanged( const Akonadi::Item &item, const QSet &partIdentifiers ); + virtual void itemRemoved( const Akonadi::Item &item ); + virtual void collectionAdded( const Akonadi::Collection &collection, const Akonadi::Collection &parent ); + virtual void collectionChanged( const Akonadi::Collection &collection ); + virtual void collectionRemoved( const Akonadi::Collection &collection ); + + virtual void itemMoved( const Akonadi::Item &item, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination ); + virtual void itemLinked( const Akonadi::Item &item, const Akonadi::Collection &collection ); + virtual void itemUnlinked( const Akonadi::Item &item, const Akonadi::Collection &collection ); + virtual void collectionMoved( const Akonadi::Collection &collection, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination ); + virtual void collectionChanged( const Akonadi::Collection &collection, const QSet &changedAttributes ); + */ + +private: + QString m_resourceId; + InvitationsCollection *m_invitationsCollection; + Akonadi::Collection m_collection; +}; + +#endif // MAILDISPATCHERAGENT_H diff --git a/agents/maildispatcher/CMakeLists.txt b/agents/maildispatcher/CMakeLists.txt new file mode 100644 index 00000000..a2213c87 --- /dev/null +++ b/agents/maildispatcher/CMakeLists.txt @@ -0,0 +1,58 @@ + +if (BUILD_TESTING) + add_subdirectory( autotests ) +endif() + +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_maildispatcher_agent\") + +set( maildispatcheragent_SRCS + maildispatcheragent.cpp + outboxqueue.cpp + sendjob.cpp + sentactionhandler.cpp + storeresultjob.cpp +) + +ecm_qt_declare_logging_category(maildispatcheragent_SRCS HEADER maildispatcher_debug.h IDENTIFIER MAILDISPATCHER_LOG CATEGORY_NAME log_maildispatcher) + +ki18n_wrap_ui(maildispatcheragent_SRCS settings.ui) +kconfig_add_kcfg_files(maildispatcheragent_SRCS settings.kcfgc) +kcfg_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/maildispatcheragent.kcfg org.kde.Akonadi.MailDispatcher.Settings) +qt5_add_dbus_adaptor(maildispatcheragent_SRCS + ${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.MailDispatcher.Settings.xml settings.h Settings +) +qt5_add_dbus_adaptor( maildispatcheragent_SRCS + org.freedesktop.Akonadi.MailDispatcherAgent.xml maildispatcheragent.h MailDispatcherAgent +) + +if (RUNTIME_PLUGINS_STATIC) + add_definitions(-DMAIL_SERIALIZER_PLUGIN_STATIC) +endif () + +add_executable(akonadi_maildispatcher_agent ${maildispatcheragent_SRCS}) + +if (RUNTIME_PLUGINS_STATIC) + target_link_libraries(akonadi_maildispatcher_agent akonadi_serializer_mail) +endif () + +if( APPLE ) + set_target_properties(akonadi_maildispatcher_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_maildispatcher_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.maildispatcher") + set_target_properties(akonadi_maildispatcher_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Maildispatcher") +endif () + +target_link_libraries(akonadi_maildispatcher_agent + KF5::AkonadiCore + KF5::AkonadiMime + KF5::Mime + KF5::MailTransport + KF5::AkonadiAgentBase + KF5::NotifyConfig + KF5::DBusAddons + KF5::I18n + KF5::Notifications +) + +install( TARGETS akonadi_maildispatcher_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) +install( FILES maildispatcheragent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents" ) +install( FILES akonadi_maildispatcher_agent.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) diff --git a/agents/maildispatcher/Messages.sh b/agents/maildispatcher/Messages.sh new file mode 100755 index 00000000..87206060 --- /dev/null +++ b/agents/maildispatcher/Messages.sh @@ -0,0 +1,4 @@ +#! /bin/sh +$EXTRACTRC *.ui *.kcfg >> rc.cpp +$XGETTEXT *.cpp -o $podir/akonadi_maildispatcher_agent.pot +rm -f rc.cpp diff --git a/agents/maildispatcher/TODO b/agents/maildispatcher/TODO new file mode 100644 index 00000000..d45568ea --- /dev/null +++ b/agents/maildispatcher/TODO @@ -0,0 +1,16 @@ +* Once MoveJobs work, enable and test moving to sent-mail. Here is why it + doesn't work currently: Akonadi::Monitor sends itemMoved(...), but it is + not handled in AgentBase::Observer, and so it never gets "processed", and + is stuck in the Monitor/ChangeRecorder forever. (I.e. the resource will + not get any new notifications.) This has to be fixed in Akonadi, but that + means adding ObserverV2 or something. +* Figure out which / whether error strings should be i18n'd +* Should probably use progressMessage instead of statusMessage, but it seems + to be unimplemented in AgentInstance, and only a stub in AgentBase. +* Do something about timeouts. +* Test aborting and progress reporting for resource-based transports. + +Bugs: +* Incorrect size reporting in itemChanged() from Monitor. Leads to displaying + >100% progress. + (found with: offline, queue some, online, abort, clearerror) diff --git a/agents/maildispatcher/akonadi_maildispatcher_agent.notifyrc b/agents/maildispatcher/akonadi_maildispatcher_agent.notifyrc new file mode 100644 index 00000000..0ca9716c --- /dev/null +++ b/agents/maildispatcher/akonadi_maildispatcher_agent.notifyrc @@ -0,0 +1,197 @@ +[Global] +IconName=mail-folder-outbox +Comment=KDE e-mail client +Comment[ast]=Veceru de corréu KDE +Comment[bg]=Пощенски клиент за KDE +Comment[bs]=KDE klijent elektronske pošte +Comment[ca]=Client de correu electrònic pel KDE +Comment[ca@valencia]=Client de correu electrònic pel KDE +Comment[cs]=Klient KDE pro čtení elektronické pošty +Comment[da]=KDE e-mail-klient +Comment[de]=E-Mail-Programm für KDE +Comment[el]=Πελάτης ηλ.ταχυδρομείου KDE +Comment[en_GB]=KDE e-mail client +Comment[es]=Cliente de correo de KDE +Comment[et]=KDE e-posti klient +Comment[fi]=KDE:n sähköpostiohjelma +Comment[fr]=Logiciel KDE de courrier électronique +Comment[ga]=Cliant Ríomhphoist KDE +Comment[gl]=Cliente de correo do KDE +Comment[hu]=KDE e-mail kliens +Comment[ia]=Programma KDE de e-posta +Comment[it]=Programma KDE di posta elettronica +Comment[ja]=KDE メールクライアント +Comment[kk]=KDE эл.пошта клиенті +Comment[km]=កម្មវិធី​អ៊ីមែល​របស់ KDE +Comment[ko]=KDE 이메일 클라이언트 +Comment[lt]=KDE el. pašto klientas +Comment[lv]=KDE e-pasta klients +Comment[mr]=केडीई इ-मेल ग्राहक +Comment[nb]=KDE E-postklient +Comment[nds]=Nettpostprogramm för KDE +Comment[nl]=KDE e-mailclient +Comment[pa]=KDE ਈ-ਮੇਲ ਕਲਾਇਟ +Comment[pl]=Program pocztowy KDE +Comment[pt]=Cliente de e-mail do KDE +Comment[pt_BR]=Cliente de e-mail do KDE +Comment[ro]=Program de poștă electronică pentru KDE +Comment[ru]=Почтовый клиент KDE +Comment[sk]=KDE poštový klient +Comment[sl]=KDE-jev poštni odjemalec +Comment[sr]=КДЕ клијент е‑поште +Comment[sr@ijekavian]=КДЕ клијент е‑поште +Comment[sr@ijekavianlatin]=KDE klijent e‑pošte +Comment[sr@latin]=KDE klijent e‑pošte +Comment[sv]=KDE E-postklient +Comment[tr]=KDE e-posta istemcisi +Comment[uk]=Поштовий клієнт KDE +Comment[x-test]=xxKDE e-mail clientxx +Comment[zh_CN]=KDE 邮件客户端 +Comment[zh_TW]=KDE 收發信軟體 +Name=KDE Mail +Name[bg]=KDE поща +Name[bs]=KDE Mail +Name[ca]=Correu del KDE +Name[ca@valencia]=Correu del KDE +Name[cs]=Pošta v KDE +Name[da]=KDE Mail +Name[de]=KDE E-Mail +Name[el]=Αλληλογραφία KDE +Name[en_GB]=KDE Mail +Name[es]=Correo de KDE +Name[et]=KDE e-post +Name[fa]=نامه‌ی کی‌دی‌ای +Name[fi]=KDE Mail +Name[fr]=Messagerie KDE +Name[ga]=Ríomhphost KDE +Name[gl]=Correo do KDE +Name[hu]=KDE Mail +Name[ia]=Posta KDE +Name[it]=KDE Mail +Name[ja]=KDE Mail +Name[kk]=KDE поштасы +Name[km]=សំបុត្រ​របស់ KDE +Name[ko]=KDE 메일 +Name[lt]=KDE paštas +Name[lv]=KDE pasts +Name[mr]=केडीई मेल +Name[nb]=KDE Mail +Name[nds]=KDE-Nettpost +Name[nl]=KDE E-mail +Name[pa]=KDE ਮੇਲ +Name[pl]=Poczta KDE +Name[pt]=Correio do KDE +Name[pt_BR]=E-mail do KDE +Name[ro]=Poșta KDE +Name[ru]=Почта KDE +Name[sk]=KDE Mail +Name[sl]=KDE-jeva pošta +Name[sr]=КДЕ пошта +Name[sr@ijekavian]=КДЕ пошта +Name[sr@ijekavianlatin]=KDE pošta +Name[sr@latin]=KDE pošta +Name[sv]=KDE:s e-postprogram +Name[tr]=KDE E-Posta +Name[uk]=Пошта KDE +Name[x-test]=xxKDE Mailxx +Name[zh_CN]=KDE 电子邮件 +Name[zh_TW]=KDE 郵件軟體 + +[Event/emailsent] +Name=E-mail successfully sent +Name[bg]=Е-пощата е изпратена успешно +Name[bs]=E-mail uspješno poslan +Name[ca]=Correu electrònic enviat correctament +Name[ca@valencia]=Correu electrònic enviat correctament +Name[cs]=E-mail úspěšně odeslán +Name[da]=E-mail sendt +Name[de]=E-Mail erfolgreich gesendet +Name[el]=Η αλληλογραφία στάλθηκε με επιτυχία +Name[en_GB]=E-mail successfully sent +Name[es]=Correo enviado satisfactoriamente +Name[et]=E-kiri saadeti edukalt ära +Name[fa]=ای‌میل با موفقیت ارسال شد +Name[fi]=Sähköpostin lähetys onnistui +Name[fr]=Courrier électronique envoyé avec succès +Name[gl]=Enviouse a mensaxe sen problemas +Name[hu]=Az e-mail sikeresen elküldve +Name[ia]=E-posta inviate successosemente +Name[it]=Messaggio di posta inviato con successo +Name[kk]=Эл.пошта сәтті жіберілді +Name[km]=បាន​ផ្ញើ​អ៊ីមែល​ដោយ​ជោគជ័យ +Name[ko]=이메일을 성공적으로 전송함 +Name[lt]=El. laiškas sėkmingai išsiųstas +Name[lv]=E-pasta veiksmīgi nosūtīts +Name[nb]=E.post vellykket sendt +Name[nds]=Nettbreef mit Spood loosstüert +Name[nl]=E-mailbericht met succes verzonden +Name[pa]=ਈਮੇਲ ਠੀਕ ਤਰ੍ਹਾਂ ਭੇਜੀ ਗਈ +Name[pl]=Poczta została wysłana pomyślnie +Name[pt]=O e-mail foi enviado com sucesso +Name[pt_BR]=E-mail enviado com sucesso +Name[ro]=Scrisoare expediată cu succes +Name[ru]=Почта успешно отправлена +Name[sk]=E-mail úspešne odoslaný +Name[sl]=E-pošta uspešno poslana +Name[sr]=Е‑пошта је успешно послата +Name[sr@ijekavian]=Е‑пошта је успешно послата +Name[sr@ijekavianlatin]=E‑pošta je uspešno poslata +Name[sr@latin]=E‑pošta je uspešno poslata +Name[sv]=E-post skickad med lyckat resultat +Name[tr]=E-posta başarıyla gönderildi +Name[ug]=ئېلخەت مۇۋەپپەقىيەتلىك يوللاندى +Name[uk]=Пошту успішно надіслано +Name[x-test]=xxE-mail successfully sentxx +Name[zh_CN]=邮件已成功发送 +Name[zh_TW]=電子郵件已成功傳送 +Action=Popup +IconName=mail-folder-outbox + +[Event/sendingfailed] +Name=E-mail sending failed +Name[bg]=Грешка при изпращане на е-пощата +Name[bs]=Slanje elektronske pošte neuspjelo +Name[ca]=Ha fallat en enviar el correu electrònic +Name[ca@valencia]=Ha fallat en enviar el correu electrònic +Name[cs]=Odesílání e-mailu selhalo +Name[da]=Afsendelse af e-mail mislykkedes +Name[de]=Fehler beim Versenden der E-Mail +Name[el]=Η αποστολή της αλληλογραφίας απέτυχε +Name[en_GB]=E-mail sending failed +Name[es]=Fallo en el envío del correo +Name[et]=E-kirja saatmine nurjus +Name[fa]=ارسال ای‌میل شکست خورد +Name[fi]=Sähköpostin lähetys epäonnistui +Name[fr]=Erreur lors de l'envoi du courrier électronique +Name[gl]=Produciuse un fallo ao enviar o correo +Name[hu]=Az e-mail küldése sikertelen +Name[ia]=Expedition de e-posta falleva +Name[it]=Invio del messaggio di posta non riuscito +Name[kk]=Эл.поштаны жолдау жаңылысы +Name[km]=បាន​បរាជ័យ​ក្នុង​ការ​ផ្ញើ​អ៊ីមែល +Name[ko]=이메일 전송 실패 +Name[lt]=El. pašto siuntimas nepavyko +Name[lv]=E-pasta sūtīšana neizdevās +Name[nb]=E-postsending mislyktes +Name[nds]=Nettbreef lett sik nich loosstüern +Name[nl]=Verzenden van e-mail is mislukt +Name[pa]=ਈਮੇਲ ਭੇਜਣ ਲਈ ਫੇਲ੍ਹ +Name[pl]=Poczta nie została wysłana pomyślnie +Name[pt]=O envio do e-mail foi mal-sucedido +Name[pt_BR]=Falha no envio do e-mail +Name[ro]=Expedierea scrisorii a eșuat +Name[ru]=Не удалось отправить почту +Name[sk]=Poslanie správy zlyhalo +Name[sl]=Pošiljanje e-pošte ni uspelo +Name[sr]=Слање е‑поште није успело +Name[sr@ijekavian]=Слање е‑поште није успело +Name[sr@ijekavianlatin]=Slanje e‑pošte nije uspelo +Name[sr@latin]=Slanje e‑pošte nije uspelo +Name[sv]=Misslyckades skicka brev +Name[tr]=E-posta gönderimi başarısız oldu +Name[uk]=Спроба надсилання повідомлення зазнала невдачі +Name[x-test]=xxE-mail sending failedxx +Name[zh_CN]=发送邮件失败 +Name[zh_TW]=傳送信件失敗 +Action=Popup +IconName=mail-mark-junk diff --git a/agents/maildispatcher/autotests/CMakeLists.txt b/agents/maildispatcher/autotests/CMakeLists.txt new file mode 100644 index 00000000..52f03c82 --- /dev/null +++ b/agents/maildispatcher/autotests/CMakeLists.txt @@ -0,0 +1,48 @@ +include(ECMMarkAsTest) + +find_package(Qt5Test CONFIG REQUIRED) + +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +# Stolen from kdepimlibs/akonadi/tests +macro(add_akonadi_isolated_test _source) + get_filename_component(_targetName ${_source} NAME_WE) + set(_srcList ${_source} ) + + add_executable(${_targetName} ${_srcList}) + ecm_mark_as_test(maildispatcher-${_targetName}) + target_link_libraries(${_targetName} + Qt5::Test + KF5::AkonadiCore + KF5::AkonadiMime + KF5::MailTransport + KF5::Mime + KF5::I18n + KF5::ConfigGui + Qt5::DBus + Qt5::Widgets + ) + + # based on kde4_add_unit_test + if (WIN32) + get_target_property( _loc ${_targetName} LOCATION ) + set(_executable ${_loc}.bat) + else () + set(_executable ${EXECUTABLE_OUTPUT_PATH}/${_targetName}) + endif () + if (UNIX) + set(_executable ${_executable}.shell) + endif () + + find_program(_testrunner akonaditest) + + if (KDEPIM_RUN_ISOLATED_TESTS) + add_test( maildispatcheragent-${_targetName} ${_testrunner} -c ${CMAKE_CURRENT_SOURCE_DIR}/unittestenv/config.xml ${_executable} ) + endif () +endmacro(add_akonadi_isolated_test) + + + +add_akonadi_isolated_test( aborttest.cpp ) +add_akonadi_isolated_test( dupetest.cpp ) + diff --git a/agents/maildispatcher/autotests/TODO b/agents/maildispatcher/autotests/TODO new file mode 100644 index 00000000..1cdff105 --- /dev/null +++ b/agents/maildispatcher/autotests/TODO @@ -0,0 +1,6 @@ +* test online / offline +* figure out why ksyscoca is started (it's not the wallet apparently) +* aborttest: test aborting when there is >1 message queued +* test the various SendBehaviours and DispatchModes +* dupetest -> probably faster and more effective if I just use random intervals + of time between queuing messages diff --git a/agents/maildispatcher/autotests/aborttest.cpp b/agents/maildispatcher/autotests/aborttest.cpp new file mode 100644 index 00000000..5c667b46 --- /dev/null +++ b/agents/maildispatcher/autotests/aborttest.cpp @@ -0,0 +1,239 @@ +/* + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "aborttest.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define SPAM_ADDRESS "idanoka@gmail.com" +// NOTE: This test relies on a large SMTP message taking long enough to deliver, +// for it to call abort. So we need a valid receiver and a not-too-fast connection. +#define MESSAGE_MB 1 + +using namespace Akonadi; +using namespace KMime; +using namespace MailTransport; + +void AbortTest::initTestCase() +{ + QVERIFY(Control::start()); + QTest::qWait(1000); + + qRegisterMetaType(); + qRegisterMetaType(); + + // Get the outbox and clear it. + SpecialMailCollectionsRequestJob *rjob = new SpecialMailCollectionsRequestJob(this); + rjob->requestDefaultCollection(SpecialMailCollections::Outbox); + QSignalSpy spy(rjob, SIGNAL(result(KJob*))); + spy.wait(0); + outbox = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Outbox); + QVERIFY(outbox.isValid()); + ItemDeleteJob *djob = new ItemDeleteJob(outbox); + djob->exec(); // may give error if outbox empty + + // Verify transports. + akoTid = TransportManager::self()->defaultTransportId(); + Transport *t = TransportManager::self()->transportById(akoTid); + QVERIFY(t); + QCOMPARE(t->type(), int(Transport::EnumType::Akonadi)); + QList tids = TransportManager::self()->transportIds(); + tids.removeAll(akoTid); + QCOMPARE(tids.count(), 1); + smtpTid = tids.first(); + t = TransportManager::self()->transportById(smtpTid); + QVERIFY(t); + QCOMPARE(t->type(), int(Transport::EnumType::SMTP)); + + // Set sink collection. + t = TransportManager::self()->transportById(akoTid); + const QString rid = t->host(); + const AgentInstance agent = AgentManager::self()->instance(rid); + QVERIFY(agent.isValid()); + CollectionPathResolver *resolver = new CollectionPathResolver(QStringLiteral("sink"), this); + QVERIFY(resolver->exec()); + sink = Collection(resolver->collection()); + QVERIFY(sink.isValid()); + QDBusInterface conf(QLatin1String("org.freedesktop.Akonadi.Resource.") + rid, + QStringLiteral("/Settings"), QStringLiteral("org.kde.Akonadi.MailTransportDummy.Settings")); + QVERIFY(conf.isValid()); + QDBusReply reply = conf.call(QStringLiteral("setSink"), sink.id()); + QVERIFY(reply.isValid()); + agent.reconfigure(); + + // Watch sink collection. + monitor = new Monitor(this); + monitor->setCollectionMonitored(sink); +} + +void AbortTest::testAbort() +{ + // Get the MDA interface. + DispatcherInterface iface; + QVERIFY(iface.dispatcherInstance().isValid()); + QVERIFY(iface.dispatcherInstance().isOnline()); + + // Create a large message. + qDebug() << "Building message."; + Message::Ptr msg = Message::Ptr(new Message); + QByteArray line(70, 'a'); + line.append("\n"); + QByteArray content("\n"); + for (int i = 0; i < MESSAGE_MB * 1024 * 1024 / line.length() + 10; i++) { + content.append(line); + } + QVERIFY(content.length() > MESSAGE_MB * 1024 * 1024); // >10MiB + msg->setContent(content); + + // Queue the message. + qDebug() << "Queuing message."; + MessageQueueJob *qjob = new MessageQueueJob(this); + qjob->setMessage(msg); + qjob->transportAttribute().setTransportId(smtpTid); + // default dispatch mode + // default sent-mail collection + qjob->addressAttribute().setFrom(QStringLiteral("naiba")); + qjob->addressAttribute().setTo(QStringList() << QStringLiteral(SPAM_ADDRESS)); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + AKVERIFYEXEC(qjob); + + // Wait for the MDA to begin dispatching. + for (int ds = 0; iface.dispatcherInstance().status() == AgentInstance::Idle; ds++) { + QTest::qWait(100); + if (ds % 10 == 0) { + qDebug() << "Waiting for the MDA to begin dispatching." << ds / 10 << "seconds elapsed."; + } + + QVERIFY2(ds <= 100, "Timeout"); + } + QTest::qWait(100); + + // Tell the MDA to abort. + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Running); + iface.dispatcherInstance().abortCurrentTask(); + for (int ds = 0; iface.dispatcherInstance().status() != AgentInstance::Idle; ds++) { + QTest::qWait(100); + if (ds % 10 == 0) { + qDebug() << "Waiting for the MDA to become idle after aborting." << ds / 10 << "seconds elapsed."; + } + + QVERIFY2(ds <= 100, "Timeout"); + } + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + + // Verify that item has an ErrorAttribute. + ItemFetchJob *fjob = new ItemFetchJob(outbox); + fjob->fetchScope().fetchAllAttributes(); + AKVERIFYEXEC(fjob); + QCOMPARE(fjob->items().count(), 1); + Item item = fjob->items().at(0); + QVERIFY(item.hasAttribute()); + ErrorAttribute *eA = item.attribute(); + qDebug() << "Stored error:" << eA->message(); + + // "Fix" the item and send again, this time with the default (Akonadi) transport. + item.removeAttribute(); + item.clearFlag(Akonadi::MessageFlags::HasError); + item.setFlag(Akonadi::MessageFlags::Queued); + TransportAttribute *newTA = new TransportAttribute(akoTid); + item.addAttribute(newTA); + ItemModifyJob *cjob = new ItemModifyJob(item); + QSignalSpy *addSpy = new QSignalSpy(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))); + AKVERIFYEXEC(cjob); + + // Verify that the item got sent. + for (int ds = 0; addSpy->isEmpty(); ds++) { + QTest::qWait(100); + if (ds % 10 == 0) { + qDebug() << "Waiting for an item to be sent." << ds / 10 << "seconds elapsed."; + } + + QVERIFY2(ds <= 100, "Timeout"); + } + QCOMPARE(addSpy->count(), 1); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); +} + +void AbortTest::testAbortWhileIdle() +{ + // Get the MDA interface. + DispatcherInterface iface; + QVERIFY(iface.dispatcherInstance().isValid()); + QVERIFY(iface.dispatcherInstance().isOnline()); + + // Abort thin air. + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + iface.dispatcherInstance().abortCurrentTask(); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + + // Queue a message (to check that subsequent messages are being sent). + QVERIFY(monitor); + QSignalSpy *addSpy = new QSignalSpy(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))); + Message::Ptr msg = Message::Ptr(new Message); + msg->setContent("\ntestAbortWhileIdle"); + MessageQueueJob *qjob = new MessageQueueJob(this); + qjob->setMessage(msg); + qjob->transportAttribute().setTransportId(akoTid); + // default dispatch mode + // default sent-mail collection + qjob->addressAttribute().setFrom(QStringLiteral("naiba")); + qjob->addressAttribute().setTo(QStringList() << QStringLiteral("dracu")); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); + AKVERIFYEXEC(qjob); + + // Verify that the item got sent. + for (int s = 0; addSpy->isEmpty(); s++) { + QTest::qWait(1000); + QVERIFY2(s <= 10, "Timeout"); + } + QCOMPARE(addSpy->count(), 1); + QCOMPARE(iface.dispatcherInstance().status(), AgentInstance::Idle); +} + +QTEST_AKONADIMAIN(AbortTest) + diff --git a/agents/maildispatcher/autotests/aborttest.h b/agents/maildispatcher/autotests/aborttest.h new file mode 100644 index 00000000..3235a965 --- /dev/null +++ b/agents/maildispatcher/autotests/aborttest.h @@ -0,0 +1,54 @@ +/* + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef ABORTTEST_H +#define ABORTTEST_H + +#include + +#include + +namespace Akonadi +{ +class Monitor; +} + +/** + This attempts to send a large message, then aborts it, then tries to send + it again and verify that it succeeds. + */ +class AbortTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testAbort(); + void testAbortWhileIdle(); + +private: + int akoTid; + int smtpTid; + Akonadi::Collection outbox; + Akonadi::Collection sink; + Akonadi::Monitor *monitor; + +}; + +#endif diff --git a/agents/maildispatcher/autotests/dupetest.cpp b/agents/maildispatcher/autotests/dupetest.cpp new file mode 100644 index 00000000..a1f4b671 --- /dev/null +++ b/agents/maildispatcher/autotests/dupetest.cpp @@ -0,0 +1,220 @@ +/* + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "dupetest.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +static const int TIMEOUT_SECONDS = 60; +static const int MAXCOUNT = 99; // must be 2-digit! + +using namespace Akonadi; +using namespace KMime; +using namespace MailTransport; + +void DupeTest::initTestCase() +{ + QVERIFY(Control::start()); + QTest::qWait(1000); // give the MDA time to start + + qRegisterMetaType(); + qRegisterMetaType(); + + // we need a default Akonadi transport + int tid = TransportManager::self()->defaultTransportId(); + Transport *t = TransportManager::self()->transportById(tid); + QVERIFY(t); + QCOMPARE(t->type(), int(Transport::EnumType::Akonadi)); + + // set the sink collection + const QString rid = t->host(); + const AgentInstance agent = AgentManager::self()->instance(rid); + QVERIFY(agent.isValid()); + CollectionPathResolver *resolver = new CollectionPathResolver(QStringLiteral("sink"), this); + QVERIFY(resolver->exec()); + sink = Collection(resolver->collection()); + QVERIFY(sink.isValid()); + QDBusInterface conf(QLatin1String("org.freedesktop.Akonadi.Resource.") + rid, + QStringLiteral("/Settings"), QStringLiteral("org.kde.Akonadi.MailTransportDummy.Settings")); + QVERIFY(conf.isValid()); + QDBusReply reply = conf.call(QStringLiteral("setSink"), sink.id()); + QVERIFY(reply.isValid()); + agent.reconfigure(); + + // set up monitor + monitor = new Monitor(this); + monitor->setCollectionMonitored(sink); + monitor->itemFetchScope().fetchFullPayload(); +} + +void DupeTest::testDupes_data() +{ + QTest::addColumn("message"); // the prefix of the message to send (-msg## is appended) + QTest::addColumn("count"); // how many copies to send + QTest::addColumn("delay"); // number of ms to wait before sending next copy + + QTest::newRow("1-nodelay") << "\n1-nodelay" << 1 << 0; + QTest::newRow("2-nodelay") << "\n2-nodelay" << 2 << 0; + QTest::newRow("5-nodelay") << "\n5-nodelay" << 5 << 0; + QTest::newRow("10-nodelay") << "\n10-nodelay" << 10 << 0; + QTest::newRow("20-nodelay") << "\n20-nodelay" << 20 << 0; + QTest::newRow("50-nodelay") << "\n50-nodelay" << 50 << 0; + QTest::newRow("99-nodelay") << "\n99-nodelay" << 99 << 0; + QTest::newRow("2-veryshortdelay") << "\n2-veryshortdelay" << 2 << 20; + QTest::newRow("5-veryshortdelay") << "\n5-veryshortdelay" << 5 << 20; + QTest::newRow("10-veryshortdelay") << "\n10-veryshortdelay" << 10 << 20; + QTest::newRow("20-veryshortdelay") << "\n20-veryshortdelay" << 20 << 20; + QTest::newRow("50-veryshortdelay") << "\n50-veryshortdelay" << 50 << 20; + QTest::newRow("99-veryshortdelay") << "\n99-veryshortdelay" << 99 << 20; + QTest::newRow("2-shortdelay") << "\n2-shortdelay" << 2 << 100; + QTest::newRow("5-shortdelay") << "\n5-shortdelay" << 5 << 100; + QTest::newRow("10-shortdelay") << "\n10-shortdelay" << 10 << 100; + QTest::newRow("20-shortdelay") << "\n20-shortdelay" << 20 << 100; + QTest::newRow("50-shortdelay") << "\n50-shortdelay" << 50 << 100; + QTest::newRow("99-shortdelay") << "\n99-shortdelay" << 99 << 99; + QTest::newRow("2-longdelay") << "\n2-longdelay" << 2 << 1000; + QTest::newRow("5-longdelay") << "\n5-longdelay" << 5 << 1000; + QTest::newRow("5-verylongdelay") << "\n5-verylongdelay" << 5 << 4000; + Q_ASSERT(99 <= MAXCOUNT); + Q_ASSERT(MAXCOUNT < 100); // 2-digit + + // TODO I'm not sure more items means a better test + // TODO test large items + // TODO test modifying items while they are being sent... +} + +void DupeTest::testDupes() +{ + QFETCH(QString, message); + QFETCH(int, count); + QFETCH(int, delay); + + // clean sink + ItemFetchJob *fjob = new ItemFetchJob(sink, this); + AKVERIFYEXEC(fjob); + if (fjob->items().count() > 0) { + // this test is needed because ItemDeleteJob gives error if no items are found + ItemDeleteJob *djob = new ItemDeleteJob(sink, this); + AKVERIFYEXEC(djob); + } + fjob = new ItemFetchJob(sink, this); + AKVERIFYEXEC(fjob); + QCOMPARE(fjob->items().count(), 0); + + // queue messages + Q_ASSERT(monitor); + QSignalSpy *addSpy = new QSignalSpy(monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection))); + qDebug() << "Queuing" << count << "messages..."; + for (int i = 0; i < count; i++) { + //qDebug() << "Queuing message" << i + 1 << "of" << count; + + Message::Ptr msg = Message::Ptr(new Message); + msg->setContent(QStringLiteral("%1-msg%2\n").arg(message).arg(i + 1, 2, 10, QLatin1Char('0')).toLatin1()); + + MessageQueueJob *job = new MessageQueueJob(this); + job->setMessage(msg); + job->transportAttribute().setTransportId(TransportManager::self()->defaultTransportId()); + // default dispatch mode + // default sent-mail collection + job->addressAttribute().setFrom(QStringLiteral("naiba")); + job->addressAttribute().setTo(QStringList() << QStringLiteral("dracu")); + //AKVERIFYEXEC( job ); + job->start(); + QTest::qWait(delay); + } + qDebug() << "Queued" << count << "messages."; + + // wait for the MDA to send them + int seconds = 0; + while (true) { + seconds++; + QTest::qWait(1000); + qDebug() << seconds << "seconds elapsed." << addSpy->count() << "messages got to sink."; + if (addSpy->count() >= count) { + break; + } + +#if 0 + if (seconds >= TIMEOUT_SECONDS) { + qDebug() << "Timeout, gdb master!"; + QTest::qWait(1000 * 1000); + } +#endif + QVERIFY2(seconds < TIMEOUT_SECONDS, "Timeout"); + } + + // TODO I should verify that the MDA has actually finished its work and has an empty queue + QTest::qWait(2000); + + // verify what has been sent + fjob = new ItemFetchJob(sink, this); + fjob->fetchScope().fetchFullPayload(); + AKVERIFYEXEC(fjob); + const Item::List items = fjob->items(); + int found[ MAXCOUNT ]; + for (int i = 0; i < count; i++) { + found[i] = 0; + } + for (int i = 0; i < items.count(); i++) { + QVERIFY(items[i].hasPayload()); + Message::Ptr msg = items[i].payload(); + const QByteArray content = msg->encodedContent(); + //qDebug() << "i" << i << "content" << content; + int loc = content.indexOf("-msg"); + QVERIFY(loc >= 0); + bool ok; + int who = content.mid(loc + 4, 2).toInt(&ok); + QVERIFY(ok); + //qDebug() << "identified msg" << who; + QVERIFY(who > 0 && who <= count); + found[ who - 1 ]++; + } + for (int i = 0; i < count; i++) { + if (found[i] > 1) { + qDebug() << "found duplicate message" << i + 1 << "(" << found[i] << "times )"; + } else if (found[i] < 1) { + qDebug() << "didn't find message" << i + 1; + } + QCOMPARE(found[i], 1); + } + QCOMPARE(addSpy->count(), count); + QCOMPARE(items.count(), count); +} + +QTEST_AKONADIMAIN(DupeTest) + diff --git a/agents/maildispatcher/autotests/dupetest.h b/agents/maildispatcher/autotests/dupetest.h new file mode 100644 index 00000000..b78516df --- /dev/null +++ b/agents/maildispatcher/autotests/dupetest.h @@ -0,0 +1,48 @@ +/* + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef DUPETEST_H +#define DUPETEST_H + +#include + +#include +#include + +/** + This queues a bunch of messages very quickly one after the other, lets the + MDA send them via the dummy mailtransport resource, and then verify that the + correct number of messages has been sent. + */ +class DupeTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void testDupes_data(); + void testDupes(); + +private: + Akonadi::Collection sink; + Akonadi::Monitor *monitor; + +}; + +#endif diff --git a/agents/maildispatcher/autotests/unittestenv/config.xml b/agents/maildispatcher/autotests/unittestenv/config.xml new file mode 100644 index 00000000..248ebb58 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/config.xml @@ -0,0 +1,7 @@ + + kdehome + xdgconfig + xdglocal + akonadi_knut_resource + akonadi_mailtransport_dummy_resource + diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc new file mode 100644 index 00000000..1cac492a --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi-firstrunrc @@ -0,0 +1,3 @@ +[ProcessedDefaults] +defaultaddressbook=done +defaultcalendar=done diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc new file mode 100644 index 00000000..fb457b18 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_knut_resource_0rc @@ -0,0 +1,4 @@ +[General] +DataFile[$e]=$KDEHOME/testdata.xml +FileWatchingEnabled=false + diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_mailtransport_dummy_resource_0rc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_mailtransport_dummy_resource_0rc new file mode 100644 index 00000000..6d160492 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/akonadi_mailtransport_dummy_resource_0rc @@ -0,0 +1,2 @@ +[General] +Sink=123 diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdebugrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdebugrc new file mode 100644 index 00000000..32317f74 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdebugrc @@ -0,0 +1,110 @@ +[0] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5250] +InfoOutput=2 + +[5251] +InfoOutput=2 + +[5252] +InfoOutput=2 + +[5253] +InfoOutput=2 + +[5254] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5255] +InfoOutput=2 + +[5256] +InfoOutput=2 + +[5257] +InfoOutput=2 + +[5258] +InfoOutput=2 + +[5259] +InfoOutput=2 + +[5260] +InfoOutput=2 + +[5261] +InfoOutput=2 + +[5262] +InfoOutput=2 + +[5263] +InfoOutput=2 + +[5264] +InfoOutput=2 + +[5265] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5266] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5295] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[5324] +AbortFatal=true +ErrorFilename[$e]=kdebug.dbg +ErrorOutput=2 +FatalFilename[$e]=kdebug.dbg +FatalOutput=2 +InfoFilename[$e]=kdebug.dbg +InfoOutput=2 +WarnFilename[$e]=kdebug.dbg +WarnOutput=2 + +[7129] +InfoOutput=2 diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdedrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdedrc new file mode 100644 index 00000000..41d17814 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kdedrc @@ -0,0 +1,3 @@ +[General] +CheckSycoca=false +CheckFileStamps=false diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kwalletrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kwalletrc new file mode 100644 index 00000000..8ba29ca1 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/kwalletrc @@ -0,0 +1,2 @@ +[Wallet] +Enabled=false diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/mailtransports b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/mailtransports new file mode 100644 index 00000000..13ac7b09 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/mailtransports @@ -0,0 +1,23 @@ +[$Version] +update_info=mailtransports.upd:initial-kmail-migration,mailtransports.upd:initial-knode-migration + +[General] +default-transport=666 + +[Transport 666] +host=akonadi_mailtransport_dummy_resource_0 +id=666 +name=Dummy Akonadi Transport +type=Akonadi + +[Transport 549190884] +auth=true +encryption=SSL +host=smtp.gmail.com +id=549190884 +name=idanoka2-stored +password=ᄒᄡᄚᄆᄒᄏᄊᆱᄎᆲᆱ +port=465 +storepass=true +user=idanoka2@gmail.com + diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/qttestrc b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/qttestrc new file mode 100644 index 00000000..2e2f28ea --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/share/config/qttestrc @@ -0,0 +1,2 @@ +[Notification Messages] +WalletMigrate=false diff --git a/agents/maildispatcher/autotests/unittestenv/kdehome/testdata.xml b/agents/maildispatcher/autotests/unittestenv/kdehome/testdata.xml new file mode 100644 index 00000000..8cf871b9 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/kdehome/testdata.xml @@ -0,0 +1,4 @@ + + + + diff --git a/agents/maildispatcher/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc b/agents/maildispatcher/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc new file mode 100644 index 00000000..7f738ce2 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/xdgconfig/akonadi/akonadiserverrc @@ -0,0 +1,4 @@ +[%General] + +[Search] +Manager=Dummy diff --git a/agents/maildispatcher/autotests/unittestenv/xdglocal/.keep b/agents/maildispatcher/autotests/unittestenv/xdglocal/.keep new file mode 100644 index 00000000..2f1d07a9 --- /dev/null +++ b/agents/maildispatcher/autotests/unittestenv/xdglocal/.keep @@ -0,0 +1 @@ +force git to include this file diff --git a/agents/maildispatcher/maildispatcheragent.cpp b/agents/maildispatcher/maildispatcheragent.cpp new file mode 100644 index 00000000..65b17bb6 --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.cpp @@ -0,0 +1,361 @@ +/* + Copyright 2008 Ingo Klöcker + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "maildispatcheragent.h" + +//#include "configdialog.h" +#include "maildispatcheragentadaptor.h" +#include "outboxqueue.h" +#include "sendjob.h" +#include "sentactionhandler.h" +#include "settings.h" +#include "settingsadaptor.h" + +#include +#include +#include + +#include +#include "maildispatcher_debug.h" +#include +#include +#include +#include + +#include +#include + +#ifdef MAIL_SERIALIZER_PLUGIN_STATIC + +Q_IMPORT_PLUGIN(akonadi_serializer_mail) +#endif + +using namespace Akonadi; + +class MailDispatcherAgent::Private +{ +public: + Private(MailDispatcherAgent *parent) + : q(parent), + queue(Q_NULLPTR), + currentJob(Q_NULLPTR), + aborting(false), + sendingInProgress(false), + sentAnything(false), + errorOccurred(false), + sentItemsSize(0), + sentActionHandler(Q_NULLPTR) + { + } + + ~Private() + { + } + + MailDispatcherAgent *const q; + + OutboxQueue *queue; + SendJob *currentJob; + Item currentItem; + bool aborting; + bool sendingInProgress; + bool sentAnything; + bool errorOccurred; + qulonglong sentItemsSize; + SentActionHandler *sentActionHandler; + + // Q_SLOTS: + void abort(); + void dispatch(); + void itemFetched(const Item &item); + void queueError(const QString &message); + void sendPercent(KJob *job, unsigned long percent); + void sendResult(KJob *job); + void emitStatusReady(); +}; + +void MailDispatcherAgent::Private::abort() +{ + if (!q->isOnline()) { + qCDebug(MAILDISPATCHER_LOG) << "Offline. Ignoring call."; + return; + } + + if (aborting) { + qCDebug(MAILDISPATCHER_LOG) << "Already aborting."; + return; + } + + if (!sendingInProgress && queue->isEmpty()) { + qCDebug(MAILDISPATCHER_LOG) << "MDA is idle."; + Q_ASSERT(q->status() == AgentBase::Idle); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Aborting..."; + aborting = true; + if (currentJob) { + currentJob->abort(); + } + // Further SendJobs will mark remaining items in the queue as 'aborted'. + } +} + +void MailDispatcherAgent::Private::dispatch() +{ + Q_ASSERT(queue); + + if (!q->isOnline() || sendingInProgress) { + qCDebug(MAILDISPATCHER_LOG) << "Offline or busy. See you later."; + return; + } + + if (!queue->isEmpty()) { + if (!sentAnything) { + sentAnything = true; + sentItemsSize = 0; + Q_EMIT q->percent(0); + } + Q_EMIT q->status(AgentBase::Running, + i18np("Sending messages (1 item in queue)...", + "Sending messages (%1 items in queue)...", queue->count())); + qCDebug(MAILDISPATCHER_LOG) << "Attempting to dispatch the next message."; + sendingInProgress = true; + queue->fetchOne(); // will trigger itemFetched + } else { + qCDebug(MAILDISPATCHER_LOG) << "Empty queue."; + if (aborting) { + // Finished marking messages as 'aborted'. + aborting = false; + sentAnything = false; + Q_EMIT q->status(AgentBase::Idle, i18n("Sending canceled.")); + QTimer::singleShot(3000, q, SLOT(emitStatusReady())); + } else { + if (sentAnything) { + // Finished sending messages in queue. + sentAnything = false; + Q_EMIT q->percent(100); + Q_EMIT q->status(AgentBase::Idle, i18n("Finished sending messages.")); + + if (!errorOccurred) { + KNotification *notify = new KNotification(QStringLiteral("emailsent")); + notify->setComponentName(QStringLiteral("akonadi_maildispatcher_agent")); + notify->setTitle(i18nc("Notification title when email was sent", "E-Mail Successfully Sent")); + notify->setText(i18nc("Notification when the email was sent", "Your E-Mail has been sent successfully.")); + notify->sendEvent(); + } + } else { + // Empty queue. + Q_EMIT q->status(AgentBase::Idle, i18n("No items in queue.")); + } + QTimer::singleShot(3000, q, SLOT(emitStatusReady())); + } + + errorOccurred = false; + } +} + +MailDispatcherAgent::MailDispatcherAgent(const QString &id) + : AgentBase(id), + d(new Private(this)) +{ + Kdelibs4ConfigMigrator migrate(QStringLiteral("maildispatcheragent")); + migrate.setConfigFiles(QStringList() << QStringLiteral("maildispatcheragentrc") << QStringLiteral("akonadi_maildispatcher_agent.notifyrc")); + migrate.migrate(); + + qCDebug(MAILDISPATCHER_LOG) << "maildispatcheragent: At your service, sir!"; +#ifdef KDEPIM_STATIC_LIBS + ___MailTransport____INIT(); +#endif + + new SettingsAdaptor(Settings::self()); + new MailDispatcherAgentAdaptor(this); + + KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/Settings"), + Settings::self(), QDBusConnection::ExportAdaptors); + + KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/MailDispatcherAgent"), + this, QDBusConnection::ExportAdaptors); + KDBusConnectionPool::threadConnection().registerService(QStringLiteral("org.freedesktop.Akonadi.MailDispatcherAgent")); + + d->queue = new OutboxQueue(this); + connect(d->queue, SIGNAL(newItems()), + this, SLOT(dispatch())); + connect(d->queue, SIGNAL(itemReady(Akonadi::Item)), + this, SLOT(itemFetched(Akonadi::Item))); + connect(d->queue, SIGNAL(error(QString)), + this, SLOT(queueError(QString))); + connect(this, SIGNAL(itemProcessed(Akonadi::Item,bool)), + d->queue, SLOT(itemProcessed(Akonadi::Item,bool))); + connect(this, SIGNAL(abortRequested()), + this, SLOT(abort())); + + d->sentActionHandler = new SentActionHandler(this); + + setNeedsNetwork(true); +} + +MailDispatcherAgent::~MailDispatcherAgent() +{ + delete d; +} + +void MailDispatcherAgent::configure(WId windowId) +{ + Q_UNUSED(windowId); + KNotifyConfigWidget::configure(Q_NULLPTR); +} + +void MailDispatcherAgent::doSetOnline(bool online) +{ + Q_ASSERT(d->queue); + if (online) { + qCDebug(MAILDISPATCHER_LOG) << "Online. Dispatching messages."; + Q_EMIT status(AgentBase::Idle, i18n("Online, sending messages in queue.")); + QTimer::singleShot(0, this, SLOT(dispatch())); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Offline."; + Q_EMIT status(AgentBase::Idle, i18n("Offline, message sending suspended.")); + + // TODO: This way, the OutboxQueue will continue to react to changes in + // the outbox, but the MDA will just not send anything. Is this what we + // want? + } + + AgentBase::doSetOnline(online); +} + +void MailDispatcherAgent::Private::itemFetched(const Item &item) +{ + qCDebug(MAILDISPATCHER_LOG) << "Fetched item" << item.id() << "; creating SendJob."; + Q_ASSERT(sendingInProgress); + Q_ASSERT(!currentItem.isValid()); + currentItem = item; + Q_ASSERT(currentJob == 0); + Q_EMIT q->itemDispatchStarted(); + + currentJob = new SendJob(item, q); + if (aborting) { + currentJob->setMarkAborted(); + } + + q->status(AgentBase::Running, i18nc("Message with given subject is being sent.", "Sending: %1", + item.payload()->subject()->asUnicodeString())); + + connect(currentJob, SIGNAL(result(KJob*)), + q, SLOT(sendResult(KJob*))); + connect(currentJob, SIGNAL(percent(KJob*,ulong)), + q, SLOT(sendPercent(KJob*,ulong))); + + currentJob->start(); +} + +void MailDispatcherAgent::Private::queueError(const QString &message) +{ + Q_EMIT q->error(message); + errorOccurred = true; + // FIXME figure out why this does not set the status to Broken, etc. +} + +void MailDispatcherAgent::Private::sendPercent(KJob *job, unsigned long) +{ + Q_ASSERT(sendingInProgress); + Q_ASSERT(job == currentJob); + // The progress here is actually the TransportJob, not the entire SendJob, + // because the post-job doesn't report progress. This should be fine, + // since the TransportJob is the lengthiest operation. + + // Give the transport 80% of the weight, and move-to-sendmail 20%. + const double transportWeight = 0.8; + + const int percent = 100 * (sentItemsSize + job->processedAmount(KJob::Bytes) * transportWeight) + / (sentItemsSize + currentItem.size() + queue->totalSize()); + + qCDebug(MAILDISPATCHER_LOG) << "sentItemsSize" << sentItemsSize + << "this job processed" << job->processedAmount(KJob::Bytes) + << "queue totalSize" << queue->totalSize() + << "total total size (sent+current+queue)" << (sentItemsSize + currentItem.size() + queue->totalSize()) + << "new percentage" << percent << "old percentage" << q->progress(); + + if (percent != q->progress()) { + // The progress can decrease too, if messages got added to the queue. + Q_EMIT q->percent(percent); + } + + // It is possible that the number of queued messages has changed. + Q_EMIT q->status(AgentBase::Running, + i18np("Sending messages (1 item in queue)...", + "Sending messages (%1 items in queue)...", 1 + queue->count())); +} + +void MailDispatcherAgent::Private::sendResult(KJob *job) +{ + Q_ASSERT(sendingInProgress); + Q_ASSERT(job == currentJob); + currentJob->disconnect(q); + currentJob = Q_NULLPTR; + + Q_ASSERT(currentItem.isValid()); + sentItemsSize += currentItem.size(); + Q_EMIT q->itemProcessed(currentItem, !job->error()); + + const Akonadi::Item sentItem = currentItem; + currentItem = Item(); + + if (job->error()) { + // The SendJob gave the item an ErrorAttribute, so we don't have to + // do anything. + qCDebug(MAILDISPATCHER_LOG) << "Sending failed. error:" << job->errorString(); + + KNotification *notify = new KNotification(QStringLiteral("sendingfailed")); + notify->setComponentName(QStringLiteral("akonadi_maildispatcher_agent")); + notify->setTitle(i18nc("Notification title when email sending failed", "E-Mail Sending Failed")); + notify->setText(job->errorString()); + notify->sendEvent(); + + errorOccurred = true; + } else { + qCDebug(MAILDISPATCHER_LOG) << "Sending succeeded."; + + // handle possible sent actions + const MailTransport::SentActionAttribute *attribute = sentItem.attribute(); + if (attribute) { + foreach (const MailTransport::SentActionAttribute::Action &action, attribute->actions()) { + sentActionHandler->runAction(action); + } + } + } + + // dispatch next message + sendingInProgress = false; + QTimer::singleShot(0, q, SLOT(dispatch())); +} + +void MailDispatcherAgent::Private::emitStatusReady() +{ + if (q->status() == AgentBase::Idle) { + // If still idle after aborting, clear 'aborted' status. + Q_EMIT q->status(AgentBase::Idle, i18n("Ready to dispatch messages.")); + } +} + +#ifndef KDEPIM_PLUGIN_AGENT +AKONADI_AGENT_MAIN(MailDispatcherAgent) +#endif + +#include "moc_maildispatcheragent.cpp" diff --git a/agents/maildispatcher/maildispatcheragent.desktop b/agents/maildispatcher/maildispatcheragent.desktop new file mode 100644 index 00000000..5fb619e0 --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.desktop @@ -0,0 +1,93 @@ +[Desktop Entry] +Name=Mail Dispatcher Agent +Name[bs]=Dispačer mail agent +Name[ca]=Agent distribuïdor de correu +Name[ca@valencia]=Agent distribuïdor de correu +Name[cs]=Agent odesílatele zpráv +Name[da]=Mailafsendingsagent (MDA) +Name[de]=Agent zur Nachrichten-Auslieferung +Name[el]=Πράκτορας αποστολής αλληλογραφίας +Name[en_GB]=Mail Dispatcher Agent +Name[es]=Agente despachador de correo +Name[et]=Kirjade edastamise agent +Name[fi]=Postinvälitysagentti +Name[fr]=Agent de diffusion de messages +Name[gl]=Axente de Despacho de Correo +Name[hu]=Levélfeladó ügynök +Name[ia]=Agente Distributor de Posta +Name[it]=Agente per la consegna della posta +Name[ja]=メール送信エージェント +Name[kk]=Пошта реттеуш агенті +Name[km]=ភ្នាក់ងារ​កម្មវិធី​បញ្ជូន​សំបុត្រ +Name[ko]=메일 가져오기 마법사 +Name[lt]=Laiškų gijų išdėstymo agentas +Name[lv]=Pasta nosūtīšanas aģents +Name[nb]=Agent for e-postsending +Name[nds]=Nettpostverdeel-Hölper +Name[nl]=Agent voor het verzenden van e-mail +Name[pa]=ਮੇਲ ਡਿਸਪੈਚਰ ਏਜੰਟ +Name[pl]=Agent przyjmowania poczty +Name[pt]=Agente de Despacho do Correio +Name[pt_BR]=Agente de encaminhamento de e-mails +Name[ro]=Agent de livrare a mesajelor +Name[ru]=Агент почтового диспетчера +Name[sk]=Agent spracovania pošty +Name[sl]=Posrednik za razpošiljanje pošte +Name[sr]=Агент отпремања поште +Name[sr@ijekavian]=Агент отпремања поште +Name[sr@ijekavianlatin]=Agent otpremanja pošte +Name[sr@latin]=Agent otpremanja pošte +Name[sv]=E-postsändningsmodul +Name[tr]=E-posta Yönetim Aracı +Name[ug]=Mail Dispatcher Agent +Name[uk]=Агент розподілу пошти +Name[x-test]=xxMail Dispatcher Agentxx +Name[zh_CN]=邮件签发代理 +Name[zh_TW]=郵件配送代理程式 +Comment=Dispatches email messages +Comment[bs]=Raspoređuje E-mail poruke +Comment[ca]=Distribueix missatges de correu electrònic +Comment[ca@valencia]=Distribueix missatges de correu electrònic +Comment[cs]=Odesílá e-mailové zprávy +Comment[da]=Udsender e-mails +Comment[de]=Verteilt E-Mail-Nachrichten +Comment[el]=Διανέμει μηνύματα ηλ. ταχυδρομείου +Comment[en_GB]=Dispatches email messages +Comment[es]=Remite mensajes de correo +Comment[et]=Kirjade edastamine +Comment[fi]=Lähettää sähköpostiviestejä +Comment[fr]=Diffuse les courriers électroniques +Comment[gl]=Xestiona mensaxes de correo. +Comment[hu]=E-mail üzeneteket kézbesít +Comment[ia]=Expedi messages de e-posta +Comment[it]=Invia messaggi di posta elettronica +Comment[kk]=Пошта хаттарын үлестіру +Comment[ko]=이메일 메시지를 가져옴 +Comment[lt]=Išsiunčia el. laiškus +Comment[nb]=Sender ut e-postmeldinger +Comment[nds]=Verdeelt Nettpost +Comment[nl]=Verstuurt e-mailberichten +Comment[pl]=Rozsyła wiadomości pocztowe +Comment[pt]=Trata das mensagens de e-mail +Comment[pt_BR]=Encaminhamento de e-mails +Comment[ro]=Remite scrisori electronice +Comment[ru]=Рассылает почтовые сообщения +Comment[sk]=Vybavuje e-mailové správy +Comment[sl]=Odpošlje e-poštna sporočila +Comment[sr]=Отпрема е‑поштанске поруке +Comment[sr@ijekavian]=Отпрема е‑поштанске поруке +Comment[sr@ijekavianlatin]=Otprema e‑poštanske poruke +Comment[sr@latin]=Otprema e‑poštanske poruke +Comment[sv]=Skickar brev med e-post +Comment[tr]=E-posta mesajlarını yollar +Comment[uk]=Розповсюджує повідомлення електронної пошти +Comment[x-test]=xxDispatches email messagesxx +Comment[zh_CN]=发送电子邮件 +Comment[zh_TW]=分配電子郵件訊息 +Type=AkonadiAgent +Exec=akonadi_maildispatcher_agent +Icon=mail-folder-outbox + +X-Akonadi-MimeTypes=message/rfc822 +X-Akonadi-Capabilities=Unique,Autostart +X-Akonadi-Identifier=akonadi_maildispatcher_agent diff --git a/agents/maildispatcher/maildispatcheragent.h b/agents/maildispatcher/maildispatcheragent.h new file mode 100644 index 00000000..3d944a83 --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.h @@ -0,0 +1,76 @@ +/* + Copyright 2008 Ingo Klöcker + Copyright 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef MAILDISPATCHERAGENT_H +#define MAILDISPATCHERAGENT_H + +#include + +namespace Akonadi +{ +class Item; +} + +/** + * @short This agent dispatches mail put into the outbox collection. + */ +class MailDispatcherAgent : public Akonadi::AgentBase +{ + Q_OBJECT + + Q_CLASSINFO("D-Bus Interface", "org.freedesktop.Akonadi.MailDispatcherAgent") + +public: + explicit MailDispatcherAgent(const QString &id); + ~MailDispatcherAgent(); + +public Q_SLOTS: + void configure(WId windowId) Q_DECL_OVERRIDE; + +Q_SIGNALS: + /** + * Emitted when the MDA has attempted to send an item. + */ + void itemProcessed(const Akonadi::Item &item, bool result); + + /** + * Emitted when the MDA has begun processing an item + */ + Q_SCRIPTABLE void itemDispatchStarted(); + +protected: + void doSetOnline(bool online) Q_DECL_OVERRIDE; + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void abort()) + Q_PRIVATE_SLOT(d, void dispatch()) + Q_PRIVATE_SLOT(d, void itemFetched(const Akonadi::Item &)) + Q_PRIVATE_SLOT(d, void queueError(const QString &)) + Q_PRIVATE_SLOT(d, void sendPercent(KJob *, unsigned long)) + Q_PRIVATE_SLOT(d, void sendResult(KJob *)) + Q_PRIVATE_SLOT(d, void emitStatusReady()) + //@endcond +}; + +#endif // MAILDISPATCHERAGENT_H diff --git a/agents/maildispatcher/maildispatcheragent.kcfg b/agents/maildispatcher/maildispatcheragent.kcfg new file mode 100644 index 00000000..922fd13c --- /dev/null +++ b/agents/maildispatcher/maildispatcheragent.kcfg @@ -0,0 +1,18 @@ + + + + + + + -1 + + + + -1 + + + diff --git a/agents/maildispatcher/org.freedesktop.Akonadi.MailDispatcherAgent.xml b/agents/maildispatcher/org.freedesktop.Akonadi.MailDispatcherAgent.xml new file mode 100644 index 00000000..917af22c --- /dev/null +++ b/agents/maildispatcher/org.freedesktop.Akonadi.MailDispatcherAgent.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/agents/maildispatcher/outboxqueue.cpp b/agents/maildispatcher/outboxqueue.cpp new file mode 100644 index 00000000..7a2c2cfb --- /dev/null +++ b/agents/maildispatcher/outboxqueue.cpp @@ -0,0 +1,455 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "outboxqueue.h" + +#include +#include +#include + +#include "maildispatcher_debug.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace Akonadi; +using namespace MailTransport; + +static const int OUTBOX_DISCOVERY_RETRIES = 3; // number of times we try to find or create the outbox +static const int OUTBOX_DISCOVERY_WAIT_TIME = 5000; // number of ms to wait before retrying + +/** + * @internal + */ +class OutboxQueue::Private +{ +public: + Private(OutboxQueue *qq) + : q(qq), + outbox(-1), + monitor(Q_NULLPTR), + futureTimer(Q_NULLPTR), + totalSize(0), + outboxDiscoveryRetries(0) + { + } + + OutboxQueue *const q; + + Collection outbox; + Monitor *monitor; + QList queue; + QSet futureItems; // keeps track of items removed in the meantime + QMultiMap futureMap; + QTimer *futureTimer; + qulonglong totalSize; + int outboxDiscoveryRetries; + +#if 0 + // If an item is modified externally between the moment we pass it to + // the MDA and the time the MDA marks it as sent, then we will get + // itemChanged() and may mistakenly re-add the item to the queue. + // So we ignore the item that we pass to the MDA, until the MDA finishes + // sending it. + Item currentItem; +#endif + // HACK: The above is not enough. + // Apparently change notifications are delayed sometimes (???) + // and we re-add an item long after it was sent. So keep a list of sent + // items. + // TODO debug and figure out why this happens. + QSet ignore; + + void initQueue(); + void addIfComplete(const Item &item); + + // Q_SLOTS: + void checkFuture(); + void collectionFetched(KJob *job); + void itemFetched(KJob *job); + void localFoldersChanged(); + void localFoldersRequestResult(KJob *job); + void itemAdded(const Item &item); + void itemChanged(const Item &item); + void itemMoved(const Item &item, const Collection &source, const Collection &dest); + void itemRemoved(const Item &item); + void itemProcessed(const Item &item, bool result); +}; + +void OutboxQueue::Private::initQueue() +{ + totalSize = 0; + queue.clear(); + + qCDebug(MAILDISPATCHER_LOG) << "Fetching items in collection" << outbox.id(); + ItemFetchJob *job = new ItemFetchJob(outbox); + job->fetchScope().fetchAllAttributes(); + job->fetchScope().fetchFullPayload(false); + connect(job, SIGNAL(result(KJob*)), q, SLOT(collectionFetched(KJob*))); +} + +void OutboxQueue::Private::addIfComplete(const Item &item) +{ + if (ignore.contains(item.id())) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is ignored."; + return; + } + + if (queue.contains(item)) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "already in queue!"; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute Address."; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute DispatchMode."; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute SentBehaviour."; + return; + } + + if (!item.hasAttribute()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have the required attribute Transport."; + return; + } + + if (!item.hasFlag(Akonadi::MessageFlags::Queued)) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "has no '$QUEUED' flag."; + return; + } + + const DispatchModeAttribute *dispatchModeAttribute = item.attribute(); + Q_ASSERT(dispatchModeAttribute); + if (dispatchModeAttribute->dispatchMode() == DispatchModeAttribute::Manual) { + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is queued to be sent manually."; + return; + } + + const TransportAttribute *transportAttribute = item.attribute(); + Q_ASSERT(transportAttribute); + if (transportAttribute->transport() == Q_NULLPTR) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "has invalid transport."; + return; + } + + const SentBehaviourAttribute *sentBehaviourAttribute = item.attribute(); + Q_ASSERT(sentBehaviourAttribute); + if (sentBehaviourAttribute->sentBehaviour() == SentBehaviourAttribute::MoveToCollection && + !sentBehaviourAttribute->moveToCollection().isValid()) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "has invalid sent-mail collection."; + return; + } + + // This check requires fetchFullPayload. -> slow (?) + /* + if ( !item.hasPayload() ) { + qCWarning(MAILDISPATCHER_LOG) << "Item" << item.id() << "does not have KMime::Message::Ptr payload."; + return; + } + */ + + if (dispatchModeAttribute->dispatchMode() == DispatchModeAttribute::Automatic && + dispatchModeAttribute->sendAfter().isValid() && + dispatchModeAttribute->sendAfter() > QDateTime::currentDateTime()) { + // All the above was OK, so accept it for the future. + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is accepted to be sent in the future."; + futureMap.insert(dispatchModeAttribute->sendAfter(), item); + Q_ASSERT(!futureItems.contains(item)); + futureItems.insert(item); + checkFuture(); + return; + } + + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "is accepted into the queue (size" << item.size() << ")."; + Q_ASSERT(!queue.contains(item)); + totalSize += item.size(); + queue.append(item); + Q_EMIT q->newItems(); +} + +void OutboxQueue::Private::checkFuture() +{ + qCDebug(MAILDISPATCHER_LOG) << "The future is here." << futureMap.count() << "items in futureMap."; + Q_ASSERT(futureTimer); + futureTimer->stop(); + // By default, re-check in one hour. + futureTimer->setInterval(60 * 60 * 1000); + + // Check items in ascending order of date. + while (!futureMap.isEmpty()) { + QMap::iterator it = futureMap.begin(); + qCDebug(MAILDISPATCHER_LOG) << "Item with due date" << it.key(); + if (it.key() > QDateTime::currentDateTime()) { + const int secs = QDateTime::currentDateTime().secsTo(it.key()) + 1; + qCDebug(MAILDISPATCHER_LOG) << "Future, in" << secs << "seconds."; + Q_ASSERT(secs >= 0); + if (secs < 60 * 60) { + futureTimer->setInterval(secs * 1000); + } + break; // all others are in the future too + } + if (!futureItems.contains(it.value())) { + qCDebug(MAILDISPATCHER_LOG) << "Item disappeared."; + } else { + qCDebug(MAILDISPATCHER_LOG) << "Due date is here. Queuing."; + addIfComplete(it.value()); + futureItems.remove(it.value()); + } + futureMap.erase(it); + } + + qCDebug(MAILDISPATCHER_LOG) << "Timer set to checkFuture again in" << futureTimer->interval() / 1000 << "seconds" + << "(that is" << futureTimer->interval() / 1000 / 60 << "minutes)."; + + futureTimer->start(); +} + +void OutboxQueue::Private::collectionFetched(KJob *job) +{ + if (job->error()) { + qCWarning(MAILDISPATCHER_LOG) << "Failed to fetch outbox collection. Queue will be empty until the outbox changes."; + return; + } + + const ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob); + qCDebug(MAILDISPATCHER_LOG) << "Fetched" << fetchJob->items().count() << "items."; + + foreach (const Item &item, fetchJob->items()) { + addIfComplete(item); + } +} + +void OutboxQueue::Private::itemFetched(KJob *job) +{ + if (job->error()) { + qCDebug(MAILDISPATCHER_LOG) << "Error fetching item:" << job->errorString() << ". Trying next item in queue."; + q->fetchOne(); + } + + const ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob); + if (fetchJob->items().count() != 1) { + qCDebug(MAILDISPATCHER_LOG) << "Fetched" << fetchJob->items().count() << ", expected 1. Trying next item in queue."; + q->fetchOne(); + } + + if (!fetchJob->items().isEmpty()) { + Q_EMIT q->itemReady(fetchJob->items().at(0)); + } +} + +void OutboxQueue::Private::localFoldersChanged() +{ + // Called on startup, and whenever the local folders change. + + if (SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::Outbox)) { + // Outbox is ready, init the queue from it. + const Collection collection = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::Outbox); + Q_ASSERT(collection.isValid()); + + if (outbox != collection) { + monitor->setCollectionMonitored(outbox, false); + monitor->setCollectionMonitored(collection, true); + outbox = collection; + qCDebug(MAILDISPATCHER_LOG) << "Changed outbox to" << outbox.id(); + initQueue(); + } + } else { + // Outbox is not ready. Request it, since otherwise we will not know when + // new messages appear. + // (Note that we are a separate process, so we get no notification when + // MessageQueueJob requests the Outbox.) + monitor->setCollectionMonitored(outbox, false); + outbox = Collection(-1); + + SpecialMailCollectionsRequestJob *job = new SpecialMailCollectionsRequestJob(q); + job->requestDefaultCollection(SpecialMailCollections::Outbox); + connect(job, SIGNAL(result(KJob*)), q, SLOT(localFoldersRequestResult(KJob*))); + + qCDebug(MAILDISPATCHER_LOG) << "Requesting outbox folder."; + job->start(); + } + + // make sure we have a place to dump the sent mails as well + if (!SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { + SpecialMailCollectionsRequestJob *job = new SpecialMailCollectionsRequestJob(q); + job->requestDefaultCollection(SpecialMailCollections::SentMail); + + qCDebug(MAILDISPATCHER_LOG) << "Requesting sent-mail folder"; + job->start(); + } +} + +void OutboxQueue::Private::localFoldersRequestResult(KJob *job) +{ + if (job->error()) { + // We tried to create the outbox, but that failed. This could be because some + // other process, the mail app, for example, tried to create it at the + // same time. So try again, once or twice, but wait a little in between, longer + // each time. If we still haven't managed to create it after a few retries, + // error hard. + + if (++outboxDiscoveryRetries <= OUTBOX_DISCOVERY_RETRIES) { + const int timeout = OUTBOX_DISCOVERY_WAIT_TIME * outboxDiscoveryRetries; + qCWarning(MAILDISPATCHER_LOG) << "Failed to get outbox folder. Retrying in: " << timeout; + QTimer::singleShot(timeout, q, SLOT(localFoldersChanged())); + } else { + qCWarning(MAILDISPATCHER_LOG) << "Failed to get outbox folder. Giving up.";; + Q_EMIT q->error(i18n("Could not access the outbox folder (%1).", job->errorString())); + } + return; + } + + localFoldersChanged(); +} + +void OutboxQueue::Private::itemAdded(const Item &item) +{ + addIfComplete(item); +} + +void OutboxQueue::Private::itemChanged(const Item &item) +{ + addIfComplete(item); + // TODO: if the item is moved out of the outbox, will I get itemChanged? +} + +void OutboxQueue::Private::itemMoved(const Item &item, const Collection &source, const Collection &destination) +{ + if (source == outbox) { + itemRemoved(item); + } else if (destination == outbox) { + addIfComplete(item); + } +} + +void OutboxQueue::Private::itemRemoved(const Item &removedItem) +{ + // @p item has size=0, so get the size from our own copy. + const int index = queue.indexOf(removedItem); + if (index == -1) { + // Item was not in queue at all. + return; + } + + Item item(queue.takeAt(index)); + qCDebug(MAILDISPATCHER_LOG) << "Item" << item.id() << "(size" << item.size() << ") was removed from the queue."; + totalSize -= item.size(); + + futureItems.remove(removedItem); +} + +void OutboxQueue::Private::itemProcessed(const Item &item, bool result) +{ + Q_ASSERT(ignore.contains(item.id())); + if (!result) { + // Give the user a chance to re-send the item if it failed. + ignore.remove(item.id()); + } +} + +OutboxQueue::OutboxQueue(QObject *parent) + : QObject(parent), + d(new Private(this)) +{ + d->monitor = new Monitor(this); + d->monitor->itemFetchScope().fetchAllAttributes(); + d->monitor->itemFetchScope().fetchFullPayload(false); + connect(d->monitor, SIGNAL(itemAdded(Akonadi::Item,Akonadi::Collection)), + this, SLOT(itemAdded(Akonadi::Item))); + connect(d->monitor, SIGNAL(itemChanged(Akonadi::Item,QSet)), + this, SLOT(itemChanged(Akonadi::Item))); + connect(d->monitor, SIGNAL(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection)), + this, SLOT(itemMoved(Akonadi::Item,Akonadi::Collection,Akonadi::Collection))); + connect(d->monitor, SIGNAL(itemRemoved(Akonadi::Item)), + this, SLOT(itemRemoved(Akonadi::Item))); + + connect(SpecialMailCollections::self(), SIGNAL(defaultCollectionsChanged()), this, SLOT(localFoldersChanged())); + d->localFoldersChanged(); + + d->futureTimer = new QTimer(this); + connect(d->futureTimer, SIGNAL(timeout()), this, SLOT(checkFuture())); + d->futureTimer->start(60 * 60 * 1000); // 1 hour +} + +OutboxQueue::~OutboxQueue() +{ + delete d; +} + +bool OutboxQueue::isEmpty() const +{ + return d->queue.isEmpty(); +} + +int OutboxQueue::count() const +{ + if (d->queue.count() == 0) { + // TODO Is this asking for too much? + Q_ASSERT(d->totalSize == 0); + } + return d->queue.count(); +} + +qulonglong OutboxQueue::totalSize() const +{ + return d->totalSize; +} + +void OutboxQueue::fetchOne() +{ + if (isEmpty()) { + qCDebug(MAILDISPATCHER_LOG) << "Empty queue."; + return; + } + + const Item item = d->queue.takeFirst(); + + d->totalSize -= item.size(); + Q_ASSERT(!d->ignore.contains(item.id())); + d->ignore.insert(item.id()); + + ItemFetchJob *job = new ItemFetchJob(item); + job->fetchScope().fetchAllAttributes(); + job->fetchScope().fetchFullPayload(); + connect(job, SIGNAL(result(KJob*)), this, SLOT(itemFetched(KJob*))); +} + +#include "moc_outboxqueue.cpp" diff --git a/agents/maildispatcher/outboxqueue.h b/agents/maildispatcher/outboxqueue.h new file mode 100644 index 00000000..721c5b43 --- /dev/null +++ b/agents/maildispatcher/outboxqueue.h @@ -0,0 +1,94 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef OUTBOXQUEUE_H +#define OUTBOXQUEUE_H + +#include +#include + +#include + +class KJob; + +/** + * @short Monitors the outbox collection and provides a queue of messages for the MDA to send. + */ +class OutboxQueue : public QObject +{ + Q_OBJECT + friend class MailDispatcherAgent; + +public: + /** + * Creates a new outbox queue. + * + * @param parent The parent object. + */ + explicit OutboxQueue(QObject *parent = Q_NULLPTR); + + /** + * Destroys the outbox queue. + */ + virtual ~OutboxQueue(); + + /** + * Returns whether the queue is empty. + */ + bool isEmpty() const; + + /** + * Returns the number of items in the queue. + */ + int count() const; + + /** + * Returns the size (in bytes) of all items in the queue. + */ + qulonglong totalSize() const; + + /** + * Fetches an item and emits itemReady() when done. + */ + void fetchOne(); + +Q_SIGNALS: + void itemReady(const Akonadi::Item &item); + void newItems(); + void error(const QString &error); + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void checkFuture()) + Q_PRIVATE_SLOT(d, void collectionFetched(KJob *)) + Q_PRIVATE_SLOT(d, void itemFetched(KJob *)) + Q_PRIVATE_SLOT(d, void localFoldersChanged()) + Q_PRIVATE_SLOT(d, void localFoldersRequestResult(KJob *)) + Q_PRIVATE_SLOT(d, void itemAdded(Akonadi::Item)) + Q_PRIVATE_SLOT(d, void itemChanged(Akonadi::Item)) + Q_PRIVATE_SLOT(d, void itemMoved(Akonadi::Item, Akonadi::Collection, Akonadi::Collection)) + Q_PRIVATE_SLOT(d, void itemRemoved(Akonadi::Item)) + Q_PRIVATE_SLOT(d, void itemProcessed(Akonadi::Item, bool)) + //@endcond +}; + +#endif diff --git a/agents/maildispatcher/sendjob.cpp b/agents/maildispatcher/sendjob.cpp new file mode 100644 index 00000000..d996c6ed --- /dev/null +++ b/agents/maildispatcher/sendjob.cpp @@ -0,0 +1,486 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "sendjob.h" + +#include "storeresultjob.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "maildispatcher_debug.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace Akonadi; +using namespace KMime; +using namespace MailTransport; + +/** + * Private class that helps to provide binary compatibility between releases. + * @internal + */ +class SendJob::Private +{ +public: + Private(const Item &itm, SendJob *qq) + : q(qq), + item(itm), + currentJob(Q_NULLPTR), + interface(Q_NULLPTR), + mailfilterInterface(Q_NULLPTR), + aborting(false) + { + } + + SendJob *const q; + Item item; + KJob *currentJob; + QString resourceId; + QDBusInterface *interface; + QDBusInterface *mailfilterInterface; + bool aborting; + + void doAkonadiTransport(); + void doTraditionalTransport(); + void doPostJob(bool transportSuccess, const QString &transportMessage); + void storeResult(bool success, const QString &message = QString()); + void abortPostJob(); + bool filterItem(int filterset); + + // slots + void doTransport(); + void transportPercent(KJob *job, unsigned long percent); + void transportResult(KJob *job); + void resourceProgress(const AgentInstance &instance); + void resourceResult(qlonglong itemId, int result, const QString &message); + void postJobResult(KJob *job); + void doEmitResult(KJob *job); + void slotSentMailCollectionFetched(KJob *job); +}; + +void SendJob::Private::doTransport() +{ + qCDebug(MAILDISPATCHER_LOG) << "Transporting message."; + + if (aborting) { + qCDebug(MAILDISPATCHER_LOG) << "Marking message as aborted."; + q->setError(UserDefinedError); + q->setErrorText(i18n("Message sending aborted.")); + storeResult(false, i18n("Message sending aborted.")); + return; + } + + // Is it an Akonadi transport or a traditional one? + const TransportAttribute *transportAttribute = item.attribute(); + Q_ASSERT(transportAttribute); + if (!transportAttribute->transport()) { + storeResult(false, i18n("Could not initiate message transport. Possibly invalid transport.")); + return; + } + + const TransportType type = transportAttribute->transport()->transportType(); + if (!type.isValid()) { + storeResult(false, i18n("Could not send message. Invalid transport.")); + return; + } + + if (!filterItem(8)) { //BeforeOutbound + return; + } + + if (type.type() == Transport::EnumType::Akonadi) { + // Send the item directly to the resource that will send it. + resourceId = transportAttribute->transport()->host(); + doAkonadiTransport(); + } else { + // Use a traditional transport job. + doTraditionalTransport(); + } +} + +void SendJob::Private::doAkonadiTransport() +{ + Q_ASSERT(!resourceId.isEmpty()); + Q_ASSERT(interface == 0); + + interface = new QDBusInterface( + QLatin1String("org.freedesktop.Akonadi.Resource.") + resourceId, + QStringLiteral("/Transport"), QStringLiteral("org.freedesktop.Akonadi.Resource.Transport"), + KDBusConnectionPool::threadConnection(), q); + + if (!interface->isValid()) { + storeResult(false, i18n("Failed to get D-Bus interface of resource %1.", resourceId)); + delete interface; + interface = Q_NULLPTR; + return; + } + + // Signals. + QObject::connect(AgentManager::self(), SIGNAL(instanceProgressChanged(Akonadi::AgentInstance)), + q, SLOT(resourceProgress(Akonadi::AgentInstance))); + QObject::connect(interface, SIGNAL(transportResult(qlonglong,int,QString)), + q, SLOT(resourceResult(qlonglong,int,QString))); + + // Start sending. + const QDBusReply reply = interface->call(QStringLiteral("send"), item.id()); + if (!reply.isValid()) { + storeResult(false, i18n("Invalid D-Bus reply from resource %1.", resourceId)); + return; + } +} + +void SendJob::Private::doTraditionalTransport() +{ + const TransportAttribute *transportAttribute = item.attribute(); + TransportJob *job = TransportManager::self()->createTransportJob(transportAttribute->transportId()); + + Q_ASSERT(job); + Q_ASSERT(currentJob == 0); + + currentJob = job; + + // Message. + Q_ASSERT(item.hasPayload()); + const Message::Ptr message = item.payload(); + bool needAssemble = false; + if (message->hasHeader("Bcc")) { + message->removeHeader("Bcc"); + needAssemble = true; + } + if (message->hasHeader("X-KMail-Identity")) { + message->removeHeader("X-KMail-Identity"); + needAssemble = true; + } + if (message->hasHeader("X-KMail-Dictionary")) { + message->removeHeader("X-KMail-Dictionary"); + needAssemble = true; + } + + if (needAssemble) { + message->assemble(); + } + const QByteArray content = message->encodedContent(true) + "\r\n"; + Q_ASSERT(!content.isEmpty()); + + // Addresses. + const AddressAttribute *addressAttribute = item.attribute(); + Q_ASSERT(addressAttribute); + + job->setData(content); + job->setSender(addressAttribute->from()); + job->setTo(addressAttribute->to()); + job->setCc(addressAttribute->cc()); + job->setBcc(addressAttribute->bcc()); + + // Signals. + connect(job, SIGNAL(result(KJob*)), + q, SLOT(transportResult(KJob*))); + connect(job, SIGNAL(percent(KJob*,ulong)), + q, SLOT(transportPercent(KJob*,ulong))); + + job->start(); +} + +void SendJob::Private::transportPercent(KJob *job, unsigned long) +{ + Q_ASSERT(currentJob == job); + qCDebug(MAILDISPATCHER_LOG) << "Processed amount" << job->processedAmount(KJob::Bytes) + << "total amount" << job->totalAmount(KJob::Bytes); + + q->setTotalAmount(KJob::Bytes, job->totalAmount(KJob::Bytes)); // Is not set at the time of start(). + q->setProcessedAmount(KJob::Bytes, job->processedAmount(KJob::Bytes)); +} + +void SendJob::Private::transportResult(KJob *job) +{ + Q_ASSERT(currentJob == job); + currentJob = Q_NULLPTR; + doPostJob(!job->error(), job->errorString()); +} + +void SendJob::Private::resourceProgress(const AgentInstance &instance) +{ + if (!interface) { + // We might have gotten a very late signal. + qCWarning(MAILDISPATCHER_LOG) << "called but no resource job running!"; + return; + } + + if (instance.identifier() == resourceId) { + // This relies on the resource's progress representing the progress of + // sending this item. + q->setPercent(instance.progress()); + } +} + +void SendJob::Private::resourceResult(qlonglong itemId, int result, + const QString &message) +{ + Q_UNUSED(itemId); + Q_ASSERT(interface); + delete interface; // So that abort() knows the transport job is over. + interface = Q_NULLPTR; + + const TransportResourceBase::TransportResult transportResult = + static_cast(result); + + const bool success = (transportResult == TransportResourceBase::TransportSucceeded); + + Q_ASSERT(itemId == item.id()); + doPostJob(success, message); +} + +void SendJob::Private::abortPostJob() +{ + // We were unlucky and LocalFolders is recreating its stuff right now. + // We will not wait for it. + qCWarning(MAILDISPATCHER_LOG) << "Default sent mail collection unavailable, not moving the mail after sending."; + q->setError(UserDefinedError); + q->setErrorText(i18n("Default sent-mail folder unavailable. Keeping message in outbox.")); + storeResult(false, q->errorString()); +} + +void SendJob::Private::doPostJob(bool transportSuccess, const QString &transportMessage) +{ + qCDebug(MAILDISPATCHER_LOG) << "success" << transportSuccess << "message" << transportMessage; + + if (!transportSuccess) { + qCDebug(MAILDISPATCHER_LOG) << "Error transporting."; + q->setError(UserDefinedError); + + const QString error = aborting ? i18n("Message transport aborted.") + : i18n("Failed to transport message."); + + q->setErrorText(error + QLatin1Char(' ') + transportMessage); + storeResult(false, q->errorString()); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Success transporting."; + + // Delete or move to sent-mail. + const SentBehaviourAttribute *attribute = item.attribute(); + Q_ASSERT(attribute); + + if (attribute->sentBehaviour() == SentBehaviourAttribute::Delete) { + qCDebug(MAILDISPATCHER_LOG) << "Deleting item from outbox."; + currentJob = new ItemDeleteJob(item); + QObject::connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(postJobResult(KJob*))); + } else { + if (attribute->sentBehaviour() == SentBehaviourAttribute::MoveToDefaultSentCollection) { + if (SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { + currentJob = new ItemMoveJob(item, SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::SentMail), q); + QObject::connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(postJobResult(KJob*))); + } else { + abortPostJob(); + } + } else { + qCDebug(MAILDISPATCHER_LOG) << "sentBehaviour=" << attribute->sentBehaviour() << "using collection from attribute"; + currentJob = new CollectionFetchJob(attribute->moveToCollection(), Akonadi::CollectionFetchJob::Base); + QObject::connect(currentJob, SIGNAL(result(KJob*)), + q, SLOT(slotSentMailCollectionFetched(KJob*))); + + } + } + } +} + +bool SendJob::Private::filterItem(int filterset) +{ + Q_ASSERT(mailfilterInterface == 0); + + // TODO: create on stack + mailfilterInterface = new QDBusInterface( + QStringLiteral("org.freedesktop.Akonadi.MailFilterAgent"), + QStringLiteral("/MailFilterAgent"), QStringLiteral("org.freedesktop.Akonadi.MailFilterAgent"), + KDBusConnectionPool::threadConnection(), q); + + if (!mailfilterInterface->isValid()) { + storeResult(false, i18n("Failed to get D-Bus interface of mailfilteragent.")); + delete mailfilterInterface; + mailfilterInterface = Q_NULLPTR; + return false; + } + + //Outbound = 0x2 + const QDBusReply reply = mailfilterInterface->call(QStringLiteral("filterItem"), item.id(), filterset, QString()); + if (!reply.isValid()) { + storeResult(false, i18n("Invalid D-Bus reply from mailfilteragent")); + delete mailfilterInterface; + mailfilterInterface = Q_NULLPTR; + return false; + } + + delete mailfilterInterface; + mailfilterInterface = Q_NULLPTR; + return true; +} + +void SendJob::Private::slotSentMailCollectionFetched(KJob *job) +{ + Akonadi::Collection fetchCol; + bool ok = false; + if (!job->error()) { + const CollectionFetchJob *const fetchJob = qobject_cast(job); + if (!fetchJob->collections().isEmpty()) { + fetchCol = fetchJob->collections().at(0); + ok = true; + } + } + if (!ok) { + if (!SpecialMailCollections::self()->hasDefaultCollection(SpecialMailCollections::SentMail)) { + abortPostJob(); + return; + } + fetchCol = SpecialMailCollections::self()->defaultCollection(SpecialMailCollections::SentMail); + } + currentJob = new ItemMoveJob(item, fetchCol, q); + QObject::connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(postJobResult(KJob*))); +} + +void SendJob::Private::postJobResult(KJob *job) +{ + Q_ASSERT(currentJob == job); + currentJob = Q_NULLPTR; + const SentBehaviourAttribute *attribute = item.attribute(); + Q_ASSERT(attribute); + + if (job->error()) { + qCDebug(MAILDISPATCHER_LOG) << "Error deleting or moving to sent-mail."; + + QString errorString; + switch (attribute->sentBehaviour()) { + case SentBehaviourAttribute::Delete: + errorString = + i18n("Sending succeeded, but failed to remove the message from the outbox."); + break; + default: + errorString = + i18n("Sending succeeded, but failed to move the message to the sent-mail folder."); + break; + } + q->setError(UserDefinedError); + q->setErrorText(errorString + QLatin1Char(' ') + job->errorString()); + storeResult(false, q->errorString()); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Success deleting or moving to sent-mail."; + if (!filterItem(2)) { //Outbound + return; + } + if (attribute->sentBehaviour() == SentBehaviourAttribute::Delete) { + q->emitResult(); + } else { + storeResult(true); + } + } +} + +void SendJob::Private::storeResult(bool success, const QString &message) +{ + qCDebug(MAILDISPATCHER_LOG) << "success" << success << "message" << message; + + Q_ASSERT(currentJob == 0); + currentJob = new StoreResultJob(item, success, message); + connect(currentJob, SIGNAL(result(KJob*)), q, SLOT(doEmitResult(KJob*))); +} + +void SendJob::Private::doEmitResult(KJob *job) +{ + Q_ASSERT(currentJob == job); + currentJob = Q_NULLPTR; + + if (job->error()) { + qCWarning(MAILDISPATCHER_LOG) << "Error storing result."; + q->setError(UserDefinedError); + q->setErrorText(q->errorString() + QLatin1Char(' ') + i18n("Failed to store result in item.") + QLatin1Char(' ') + job->errorString()); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Success storing result."; + // It is still possible that the transport failed. + StoreResultJob *srJob = static_cast(job); + if (!srJob->success()) { + q->setError(UserDefinedError); + q->setErrorText(srJob->message()); + } + } + + q->emitResult(); +} + +SendJob::SendJob(const Item &item, QObject *parent) + : KJob(parent), + d(new Private(item, this)) +{ +} + +SendJob::~SendJob() +{ + delete d; +} + +void SendJob::start() +{ + QTimer::singleShot(0, this, SLOT(doTransport())); +} + +void SendJob::setMarkAborted() +{ + Q_ASSERT(!d->aborting); + d->aborting = true; +} + +void SendJob::abort() +{ + setMarkAborted(); + + if (dynamic_cast(d->currentJob)) { + qCDebug(MAILDISPATCHER_LOG) << "Abort called, active transport job."; + // Abort transport. + d->currentJob->kill(KJob::EmitResult); + } else if (d->interface != Q_NULLPTR) { + qCDebug(MAILDISPATCHER_LOG) << "Abort called, propagating to resource."; + // Abort resource doing transport. + AgentInstance instance = AgentManager::self()->instance(d->resourceId); + instance.abortCurrentTask(); + } else { + qCDebug(MAILDISPATCHER_LOG) << "Abort called, but no transport job is active."; + // Either transport has not started, in which case doTransport will + // mark the item as aborted, or the item has already been sent, in which + // case there is nothing we can do. + } +} + +#include "moc_sendjob.cpp" diff --git a/agents/maildispatcher/sendjob.h b/agents/maildispatcher/sendjob.h new file mode 100644 index 00000000..b2137ba9 --- /dev/null +++ b/agents/maildispatcher/sendjob.h @@ -0,0 +1,91 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef SENDJOB_H +#define SENDJOB_H + +#include + +namespace Akonadi +{ +class Item; +} + +/** + * @short A job to send a mail + * + * This class takes a prevalidated Item with all the required attributes, + * sends it using MailTransport, and then stores the result of the sending + * operation in the item. + */ +class SendJob : public KJob +{ + Q_OBJECT + +public: + /** + * Creates a new send job. + * + * @param item The item to send. + * @param parent The parent object. + */ + explicit SendJob(const Akonadi::Item &item, QObject *parent = Q_NULLPTR); + + /** + * Destroys the send job. + */ + virtual ~SendJob(); + + /** + * Starts the job. + */ + void start() Q_DECL_OVERRIDE; + + /** + * If this function is called before the job is started, the SendJob will + * just mark the item as aborted, instead of sending it. + * Do not call this function more than once. + */ + void setMarkAborted(); + + /** + * Aborts sending the item. + * + * This will give the item an ErrorAttribute of "aborted". + * (No need to call setMarkAborted() if you call abort().) + */ + void abort(); + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void doTransport()) + Q_PRIVATE_SLOT(d, void transportPercent(KJob *, unsigned long)) + Q_PRIVATE_SLOT(d, void transportResult(KJob *)) + Q_PRIVATE_SLOT(d, void resourceProgress(const Akonadi::AgentInstance &)) + Q_PRIVATE_SLOT(d, void resourceResult(qlonglong, int, const QString &)) + Q_PRIVATE_SLOT(d, void postJobResult(KJob *)) + Q_PRIVATE_SLOT(d, void doEmitResult(KJob *)) + Q_PRIVATE_SLOT(d, void slotSentMailCollectionFetched(KJob *)) + //@endcond +}; + +#endif diff --git a/agents/maildispatcher/sentactionhandler.cpp b/agents/maildispatcher/sentactionhandler.cpp new file mode 100644 index 00000000..6073a31c --- /dev/null +++ b/agents/maildispatcher/sentactionhandler.cpp @@ -0,0 +1,72 @@ +/* + Copyright (C) 2010 Klarälvdalens Datakonsult AB, + a KDAB Group company, info@kdab.net, + author Tobias Koenig + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "sentactionhandler.h" + +#include +#include +#include +#include "maildispatcher_debug.h" + +using namespace MailTransport; + +SentActionHandler::SentActionHandler(QObject *parent) + : QObject(parent) +{ +} + +void SentActionHandler::runAction(const SentActionAttribute::Action &action) +{ + if (action.type() == SentActionAttribute::Action::MarkAsReplied || + action.type() == SentActionAttribute::Action::MarkAsForwarded) { + + const Akonadi::Item item(action.value().toLongLong()); + Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item); + connect(job, &Akonadi::ItemFetchJob::result, this, &SentActionHandler::itemFetchResult); + job->setProperty("type", static_cast(action.type())); + } +} + +void SentActionHandler::itemFetchResult(KJob *job) +{ + if (job->error()) { + qCWarning(MAILDISPATCHER_LOG) << job->errorText(); + return; + } + + Akonadi::ItemFetchJob *fetchJob = qobject_cast(job); + if (fetchJob->items().isEmpty()) { + return; + } + + Akonadi::Item item = fetchJob->items().at(0); + + const SentActionAttribute::Action::Type type = static_cast(job->property("type").toInt()); + if (type == SentActionAttribute::Action::MarkAsReplied) { + item.setFlag(Akonadi::MessageFlags::Replied); + } else if (type == SentActionAttribute::Action::MarkAsForwarded) { + item.setFlag(Akonadi::MessageFlags::Forwarded); + } + + Akonadi::ItemModifyJob *modifyJob = new Akonadi::ItemModifyJob(item); + modifyJob->setIgnorePayload(true); +} + diff --git a/agents/maildispatcher/sentactionhandler.h b/agents/maildispatcher/sentactionhandler.h new file mode 100644 index 00000000..91a14191 --- /dev/null +++ b/agents/maildispatcher/sentactionhandler.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2010 Klarälvdalens Datakonsult AB, + a KDAB Group company, info@kdab.net, + author Tobias Koenig + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef SENTACTIONHANDLER_H +#define SENTACTIONHANDLER_H + +#include + +#include + +class KJob; + +class SentActionHandler : public QObject +{ + Q_OBJECT + +public: + explicit SentActionHandler(QObject *parent = Q_NULLPTR); + + void runAction(const MailTransport::SentActionAttribute::Action &action); + +private Q_SLOTS: + void itemFetchResult(KJob *job); +}; + +#endif diff --git a/agents/maildispatcher/settings.kcfgc b/agents/maildispatcher/settings.kcfgc new file mode 100644 index 00000000..8c813ddb --- /dev/null +++ b/agents/maildispatcher/settings.kcfgc @@ -0,0 +1,8 @@ +File=maildispatcheragent.kcfg +ClassName=Settings +Mutators=true +ItemAccessors=true +SetUserTexts=true +Singleton=true +#IncludeFiles= +GlobalEnums=true diff --git a/agents/maildispatcher/settings.ui b/agents/maildispatcher/settings.ui new file mode 100644 index 00000000..e1868bb4 --- /dev/null +++ b/agents/maildispatcher/settings.ui @@ -0,0 +1,77 @@ + + + Till Adam <adam@kde.org> + ConfigDialog + + + + 0 + 0 + 400 + 250 + + + + Mail Dispatcher Agent Settings + + + + + + Select the collection to be used as outbox: + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + Select the collection to move sent messages into: + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + Qt::Vertical + + + + 20 + 13 + + + + + + + + + Akonadi::CollectionRequester + QFrame +
akonadi/collectionrequester.h
+ 1 +
+
+ + +
diff --git a/agents/maildispatcher/storeresultjob.cpp b/agents/maildispatcher/storeresultjob.cpp new file mode 100644 index 00000000..6f056818 --- /dev/null +++ b/agents/maildispatcher/storeresultjob.cpp @@ -0,0 +1,141 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "storeresultjob.h" + +#include +#include +#include +#include +#include "maildispatcher_debug.h" +#include +#include +#include + +using namespace Akonadi; +using namespace MailTransport; + +/** + * @internal + */ +class StoreResultJob::Private +{ +public: + Private(StoreResultJob *qq) + : q(qq), success(false) + { + } + + StoreResultJob *const q; + Item item; + bool success; + QString message; + + // Q_SLOTS: + void fetchDone(KJob *job); + void modifyDone(KJob *job); +}; + +void StoreResultJob::Private::fetchDone(KJob *job) +{ + if (job->error()) { + return; + } + + qCDebug(MAILDISPATCHER_LOG); + + const ItemFetchJob *fetchJob = qobject_cast(job); + Q_ASSERT(fetchJob); + if (fetchJob->items().count() != 1) { + qCritical() << "Fetched" << fetchJob->items().count() << "items, expected 1."; + q->setError(Unknown); + q->setErrorText(i18n("Failed to fetch item.")); + q->commit(); + return; + } + + // Store result in item. + Item item = fetchJob->items().at(0); + if (success) { + item.clearFlag(Akonadi::MessageFlags::Queued); + item.setFlag(Akonadi::MessageFlags::Sent); + item.setFlag(Akonadi::MessageFlags::Seen); + item.removeAttribute(); + } else { + item.setFlag(Akonadi::MessageFlags::HasError); + ErrorAttribute *errorAttribute = new ErrorAttribute(message); + item.addAttribute(errorAttribute); + + // If dispatch failed, set the DispatchModeAttribute to Manual. + // Otherwise, the user will *never* be able to try sending the mail again, + // as Send Queued Messages will ignore it. + if (item.hasAttribute()) { + item.attribute()->setDispatchMode(MailTransport::DispatchModeAttribute::Manual); + } else { + item.addAttribute(new DispatchModeAttribute(MailTransport::DispatchModeAttribute::Manual)); + } + } + + ItemModifyJob *modifyJob = new ItemModifyJob(item, q); + QObject::connect(modifyJob, SIGNAL(result(KJob*)), q, SLOT(modifyDone(KJob*))); +} + +void StoreResultJob::Private::modifyDone(KJob *job) +{ + if (job->error()) { + return; + } + + qCDebug(MAILDISPATCHER_LOG); + + q->commit(); +} + +StoreResultJob::StoreResultJob(const Item &item, bool success, const QString &message, QObject *parent) + : TransactionSequence(parent), + d(new Private(this)) +{ + d->item = item; + d->success = success; + d->message = message; +} + +StoreResultJob::~StoreResultJob() +{ + delete d; +} + +void StoreResultJob::doStart() +{ + // Fetch item in case it was modified elsewhere. + ItemFetchJob *job = new ItemFetchJob(d->item, this); + connect(job, SIGNAL(result(KJob*)), this, SLOT(fetchDone(KJob*))); +} + +bool StoreResultJob::success() const +{ + return d->success; +} + +QString StoreResultJob::message() const +{ + return d->message; +} + +#include "moc_storeresultjob.cpp" diff --git a/agents/maildispatcher/storeresultjob.h b/agents/maildispatcher/storeresultjob.h new file mode 100644 index 00000000..34269912 --- /dev/null +++ b/agents/maildispatcher/storeresultjob.h @@ -0,0 +1,75 @@ +/* + Copyright (c) 2009 Constantin Berzan + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef STORERESULTJOB_H +#define STORERESULTJOB_H + +#include + +#include + +namespace Akonadi +{ +class Item; +} + +/** + * This class stores the result of a StoreResultJob in an item. + * First, it removes the 'queued' flag. + * After that, if the result was success, it stores the 'sent' flag. + * If the result was failure, it stores the 'error' flag and an ErrorAttribute. + */ +class StoreResultJob : public Akonadi::TransactionSequence +{ + Q_OBJECT + +public: + /** + * Creates a new store result job. + * + * @param item The item to store. + * @param success Whether the mail could be dispatched or not. + * @param message An error message in case the mail could not be dispatched. + * @param parent The parent object. + */ + explicit StoreResultJob(const Akonadi::Item &item, bool success, const QString &message, QObject *parent = Q_NULLPTR); + + /** + * Destroys the store result job. + */ + virtual ~StoreResultJob(); + + bool success() const; + QString message() const; + +protected: + // reimpl from TransactionSequence + void doStart() Q_DECL_OVERRIDE; + +private: + //@cond PRIVATE + class Private; + Private *const d; + + Q_PRIVATE_SLOT(d, void fetchDone(KJob *job)) + Q_PRIVATE_SLOT(d, void modifyDone(KJob *job)) + //@endcond +}; + +#endif diff --git a/agents/migration/CMakeLists.txt b/agents/migration/CMakeLists.txt new file mode 100644 index 00000000..ca14b87d --- /dev/null +++ b/agents/migration/CMakeLists.txt @@ -0,0 +1,43 @@ + +include_directories( + ${kdepim-runtime_SOURCE_DIR}/migration + ${kdepim-runtime_SOURCE_DIR} +) + +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_migration_agent\") + +kde_enable_exceptions() + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + +set(migrationagent_SRCS + migrationagent.cpp + migrationstatuswidget.cpp + migrationexecutor.cpp + migrationscheduler.cpp + autotests/dummymigrator.cpp +) + +add_executable(akonadi_migration_agent ${migrationagent_SRCS}) + +if( APPLE ) + set_target_properties(akonadi_migration_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/../Info.plist.template) + set_target_properties(akonadi_migration_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.migrationagent") + set_target_properties(akonadi_migration_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE Akonadi Migrationagent") +endif () + +target_link_libraries(akonadi_migration_agent + gidmigration + KF5::AkonadiCore + KF5::AkonadiAgentBase + KF5::Contacts + KF5::WindowSystem + KF5::JobWidgets +) + +install(TARGETS akonadi_migration_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES migrationagent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}//akonadi/agents") + +if(BUILD_TESTING) + add_subdirectory(autotests) +endif() diff --git a/agents/migration/Messages.sh b/agents/migration/Messages.sh new file mode 100755 index 00000000..dd6f739c --- /dev/null +++ b/agents/migration/Messages.sh @@ -0,0 +1,2 @@ +#! /bin/sh +$XGETTEXT *.cpp -o $podir/akonadi_migration_agent.pot diff --git a/agents/migration/autotests/CMakeLists.txt b/agents/migration/autotests/CMakeLists.txt new file mode 100644 index 00000000..7bda437f --- /dev/null +++ b/agents/migration/autotests/CMakeLists.txt @@ -0,0 +1,12 @@ + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/.. +) + +add_executable(schedulertest schedulertest.cpp ../migrationscheduler.cpp ../migrationexecutor.cpp) +target_link_libraries(schedulertest + gidmigration + KF5::AkonadiCore + Qt5::Test +) +add_test(schedulertest schedulertest) diff --git a/agents/migration/autotests/dummymigrator.cpp b/agents/migration/autotests/dummymigrator.cpp new file mode 100644 index 00000000..1d4a1f05 --- /dev/null +++ b/agents/migration/autotests/dummymigrator.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * 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 . + * + */ + +#include "dummymigrator.h" +#include +#include + +DummyMigrator::DummyMigrator(const QString &identifier) + : MigratorBase(QLatin1String("dummymigrator") + identifier, QString(), QString()) +{} + +QString DummyMigrator::displayName() const +{ + return QStringLiteral("dummymigrator"); +} + +void DummyMigrator::startWork() +{ + qDebug(); + QTimer::singleShot(10000, this, &DummyMigrator::onTimerElapsed); +} + +void DummyMigrator::onTimerElapsed() +{ + qDebug(); + setMigrationState(Complete); +} + +bool DummyMigrator::shouldAutostart() const +{ + return true; +} + +bool DummyMigrator::canStart() +{ + return true; +} + +void DummyMigrator::pause() +{ + qDebug(); + MigratorBase::pause(); +} + +void DummyMigrator::abort() +{ + qDebug(); + MigratorBase::abort(); +} + diff --git a/agents/migration/autotests/dummymigrator.h b/agents/migration/autotests/dummymigrator.h new file mode 100644 index 00000000..30a7ec77 --- /dev/null +++ b/agents/migration/autotests/dummymigrator.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DUMMYMIGRATOR_H +#define DUMMYMIGRATOR_H + +#include + +/** + * Dummy migrator that simply completes after 10s and always autostarts. + * Add to the scheduler to play with the migrationagent. + */ +class DummyMigrator : public MigratorBase +{ + Q_OBJECT +public: + explicit DummyMigrator(const QString &identifier); + + QString displayName() const Q_DECL_OVERRIDE; + void startWork() Q_DECL_OVERRIDE; + + bool shouldAutostart() const Q_DECL_OVERRIDE; + bool canStart() Q_DECL_OVERRIDE; + void pause() Q_DECL_OVERRIDE; + + void abort() Q_DECL_OVERRIDE; +private Q_SLOTS: + void onTimerElapsed(); +}; + +#endif \ No newline at end of file diff --git a/agents/migration/autotests/schedulertest.cpp b/agents/migration/autotests/schedulertest.cpp new file mode 100644 index 00000000..11d849cf --- /dev/null +++ b/agents/migration/autotests/schedulertest.cpp @@ -0,0 +1,286 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * 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 . + * + */ + +#include +#include +#include +#include + +#include "../migrationscheduler.h" +#include + +Q_DECLARE_METATYPE(QModelIndex) + +class Testmigrator: public MigratorBase +{ + Q_OBJECT +public: + explicit Testmigrator(const QString &identifier, QObject *parent = Q_NULLPTR): + MigratorBase(QLatin1String("testmigrator") + identifier, QString(), QString(), parent), mAutostart(false) + {} + + QString displayName() const Q_DECL_OVERRIDE + { + return QStringLiteral("name"); + } + + void startWork() Q_DECL_OVERRIDE + {} + + void abort() Q_DECL_OVERRIDE { + setMigrationState(Aborted); + } + + virtual void complete() + { + setMigrationState(Complete); + } + + bool shouldAutostart() const Q_DECL_OVERRIDE + { + return mAutostart; + } + + void pause() Q_DECL_OVERRIDE { + setMigrationState(Paused); + } + + void resume() Q_DECL_OVERRIDE { + setMigrationState(InProgress); + } + + bool mAutostart; +}; + +class TestJobTracker : public KJobTrackerInterface +{ +public: + TestJobTracker() : mPercent(0) + {} + + void registerJob(KJob *job) Q_DECL_OVERRIDE { + KJobTrackerInterface::registerJob(job); + mJobs << job; + } + + void unregisterJob(KJob *job) Q_DECL_OVERRIDE { + mJobs.removeAll(job); + } + + void finished(KJob *job) Q_DECL_OVERRIDE { + mJobs.removeAll(job); + } + + void percent(KJob *job, long unsigned int percent) Q_DECL_OVERRIDE { + Q_UNUSED(job); + mPercent = percent; + } + + QList mJobs; + int mPercent; +}; + +class SchedulerTest: public QObject +{ + Q_OBJECT +private Q_SLOTS: + + void initTestcase() + { + qRegisterMetaType(); + } + + void testInsertRow() + { + MigrationScheduler scheduler; + QAbstractItemModel &model(scheduler.model()); + + QCOMPARE(model.rowCount(), 0); + + QSignalSpy rowsInsertedSpy(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); + QVERIFY(rowsInsertedSpy.isValid()); + + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + QCOMPARE(model.rowCount(), 1); + QCOMPARE(rowsInsertedSpy.count(), 1); + + QVERIFY(model.index(0, 0).isValid()); + QVERIFY(!model.index(1, 0).isValid()); + + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id2")))); + QCOMPARE(model.rowCount(), 2); + QCOMPARE(rowsInsertedSpy.count(), 2); + } + + void testDisplayName() + { + MigrationScheduler scheduler; + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + QAbstractItemModel &model(scheduler.model()); + QCOMPARE(model.data(model.index(0, 0)).toString(), QStringLiteral("name")); + } + + void testStartStop() + { + MigrationScheduler scheduler; + QSharedPointer migrator(new Testmigrator(QStringLiteral("id"))); + scheduler.addMigrator(migrator); + + scheduler.start(migrator->identifier()); + QCOMPARE(migrator->migrationState(), MigratorBase::InProgress); + + scheduler.abort(migrator->identifier()); + QCOMPARE(migrator->migrationState(), MigratorBase::Aborted); + } + + void testNoDuplicates() + { + MigrationScheduler scheduler; + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id")))); + QAbstractItemModel &model(scheduler.model()); + QCOMPARE(model.rowCount(), 1); + } + + void testMigrationStateChanged() + { + MigrationScheduler scheduler; + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id1")))); + QSharedPointer migrator(new Testmigrator(QStringLiteral("id2"))); + scheduler.addMigrator(migrator); + scheduler.addMigrator(QSharedPointer(new Testmigrator(QStringLiteral("id3")))); + QAbstractItemModel &model(scheduler.model()); + + QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex))); + QVERIFY(spy.isValid()); + migrator->start(); + + QCOMPARE(spy.count(), 1); + const QVariantList args = spy.takeFirst(); + QCOMPARE(args.at(0).value().row(), 1); + QCOMPARE(args.at(1).value().row(), 1); + } + + void testRunMultiple() + { + MigrationScheduler scheduler; + + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + scheduler.addMigrator(m1); + + QSharedPointer m2(new Testmigrator(QStringLiteral("id2"))); + scheduler.addMigrator(m2); + + scheduler.start(m1->identifier()); + scheduler.start(m2->identifier()); + + QCOMPARE(m1->migrationState(), MigratorBase::InProgress); + QCOMPARE(m2->migrationState(), MigratorBase::InProgress); + } + + void testRunAutostart() + { + MigrationScheduler scheduler; + + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + + QSharedPointer m2(new Testmigrator(QStringLiteral("id2"))); + m2->mAutostart = true; + scheduler.addMigrator(m2); + + QCOMPARE(m1->migrationState(), MigratorBase::InProgress); + qDebug() << m2->migrationState(); + QCOMPARE(m2->migrationState(), MigratorBase::None); + m1->complete(); + QCOMPARE(m2->migrationState(), MigratorBase::InProgress); + + } + + void testJobTracker() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + + QCOMPARE(jobTracker.mJobs.size(), 1); + + m1->complete(); + + //Give the job some time to delete itself + QTest::qWait(500); + + QCOMPARE(jobTracker.mJobs.size(), 0); + } + + void testSuspend() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + jobTracker.mJobs.first()->suspend(); + QCOMPARE(m1->migrationState(), MigratorBase::Paused); + jobTracker.mJobs.first()->resume(); + QCOMPARE(m1->migrationState(), MigratorBase::InProgress); + } + + /* + * Even if the migrator doesn't implement suspend, the executor suspends after completing the current job and waits with starting the second job. + */ + void testJobFinishesDuringSuspend() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + QSharedPointer m2(new Testmigrator(QStringLiteral("id2"))); + m2->mAutostart = true; + scheduler.addMigrator(m2); + jobTracker.mJobs.first()->suspend(); + m1->complete(); + QCOMPARE(m2->migrationState(), MigratorBase::None); + jobTracker.mJobs.first()->resume(); + QCOMPARE(m2->migrationState(), MigratorBase::InProgress); + } + + void testProgressReporting() + { + TestJobTracker jobTracker; + MigrationScheduler scheduler(&jobTracker); + QSharedPointer m1(new Testmigrator(QStringLiteral("id1"))); + m1->mAutostart = true; + scheduler.addMigrator(m1); + QCOMPARE(jobTracker.mPercent, 0); + m1->complete(); + QCOMPARE(jobTracker.mPercent, 100); + } + +}; + +QTEST_MAIN(SchedulerTest) + +#include "schedulertest.moc" diff --git a/agents/migration/migrationagent.cpp b/agents/migration/migrationagent.cpp new file mode 100644 index 00000000..71e9af10 --- /dev/null +++ b/agents/migration/migrationagent.cpp @@ -0,0 +1,75 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * 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 . + * + */ +#include "migrationagent.h" + +#include "migrationstatuswidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Akonadi +{ + +MigrationAgent::MigrationAgent(const QString &id) + : AgentBase(id), + mScheduler(new KUiServerJobTracker) +{ + KLocalizedString::setApplicationDomain("akonadi_migration_agent"); + mScheduler.addMigrator(QSharedPointer(new GidMigrator(KContacts::Addressee::mimeType()))); +} + +void MigrationAgent::configure(WId windowId) +{ + QDialog *dlg = new QDialog(); + QVBoxLayout *topLayout = new QVBoxLayout; + dlg->setLayout(topLayout); + + MigrationStatusWidget *widget = new MigrationStatusWidget(mScheduler, dlg); + topLayout->addWidget(widget); + dlg->setAttribute(Qt::WA_DeleteOnClose); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject); + topLayout->addWidget(buttonBox); + + dlg->setWindowTitle(i18nc("Title of the window that shows the status of the migration agent and offers controls to start/stop individual migration jobs.", "Migration Status")); + dlg->resize(600, 300); + + if (windowId) { +#ifndef Q_OS_WIN + KWindowSystem::setMainWindow(dlg, windowId); +#else + KWindowSystem::setMainWindow(dlg, (HWND)windowId); +#endif + } + dlg->show(); +} + +} + +AKONADI_AGENT_MAIN(Akonadi::MigrationAgent) + diff --git a/agents/migration/migrationagent.desktop b/agents/migration/migrationagent.desktop new file mode 100644 index 00000000..299271c2 --- /dev/null +++ b/agents/migration/migrationagent.desktop @@ -0,0 +1,45 @@ +[Desktop Entry] +Name=Migration Agent +Name[bs]=Migracijski agent +Name[ca]=Agent de migració +Name[ca@valencia]=Agent de migració +Name[da]=Migreringsagent +Name[de]=Migrations-Assistent +Name[el]=Πράκτορας μεταφοράς +Name[en_GB]=Migration Agent +Name[es]=Agente de migración +Name[et]=Migreerimisagent +Name[fi]=Siirtoagentti +Name[fr]=Agent de migration +Name[gl]=Axente de migración +Name[hu]=Költöztető ügynök +Name[ia]=Agente de migration +Name[it]=Agente di migrazione +Name[kk]=Көшіп ауысу агенті +Name[ko]=이전 마법사 +Name[lt]=Migravimo agentas +Name[nb]=Migreringsagent +Name[nds]=Ümwannelhölper +Name[nl]=Migratie-agent +Name[pl]=Agent migracji +Name[pt]=Agente de Migração +Name[pt_BR]=Agente de Migração +Name[ru]=Агент переноса данных +Name[sk]=Agent migrácie +Name[sl]=Posrednik za selitev +Name[sr]=Агент селидбе +Name[sr@ijekavian]=Агент селидбе +Name[sr@ijekavianlatin]=Agent selidbe +Name[sr@latin]=Agent selidbe +Name[sv]=Överföringsmodul +Name[tr]=Taşıma Yardımcısı +Name[uk]=Агент перенесення +Name[x-test]=xxMigration Agentxx +Name[zh_CN]=迁移助手 +Name[zh_TW]=移植代理程式 +Type=AkonadiAgent +Exec=akonadi_migration_agent +Icon=system-reboot + +X-Akonadi-Capabilities=Unique,Autostart +X-Akonadi-Identifier=akonadi_migration_agent diff --git a/agents/migration/migrationagent.h b/agents/migration/migrationagent.h new file mode 100644 index 00000000..690cdabe --- /dev/null +++ b/agents/migration/migrationagent.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MIGRATIONAGENT_H +#define MIGRATIONAGENT_H + +#include +#include "migrationscheduler.h" + +namespace Akonadi +{ + +class MigrationAgent : public AgentBase, public AgentBase::ObserverV2 +{ + Q_OBJECT +public: + explicit MigrationAgent(const QString &id); + void configure(WId windowId) Q_DECL_OVERRIDE; +private: + MigrationScheduler mScheduler; +}; + +} + +#endif diff --git a/agents/migration/migrationexecutor.cpp b/agents/migration/migrationexecutor.cpp new file mode 100644 index 00000000..fa2c5323 --- /dev/null +++ b/agents/migration/migrationexecutor.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * 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 . + * + */ +#include "migrationexecutor.h" + +#include + +MigrationExecutor::MigrationExecutor() + : KJob(), + mSuspended(false), + mTotalAmount(0), + mAlreadyProcessed(0) +{ + setCapabilities(Suspendable); +} + +void MigrationExecutor::start() +{ + setPercent(0); + Q_EMIT description(this, i18nc("User visible name of ongoing Akonadi migration jobs", "PIM Maintenance")); +} + +void MigrationExecutor::add(const QSharedPointer &migrator) +{ + mTotalAmount++; + mQueue.enqueue(migrator.toWeakRef()); + executeNext(); +} + +void MigrationExecutor::executeNext() +{ + if (mCurrentMigrator || mSuspended) { + return; + } + QSharedPointer migrator; + while (!migrator && !mQueue.isEmpty()) { + mCurrentMigrator = mQueue.dequeue(); + migrator = mCurrentMigrator.toStrongRef(); + } + if (migrator) { + Q_EMIT infoMessage(this, i18nc("PIM-Maintenance is in progress.", "In progress...")); + connect(migrator.data(), &MigratorBase::stoppedProcessing, this, &MigrationExecutor::onStoppedProcessing); + migrator->start(); + } else { + // Reset the notification status, otherwise we get notification "In progress...[finished]" + // without any description that it's related to PIM-Maintenance + Q_EMIT infoMessage(this, i18n("PIM Maintenance")); + emitResult(); + } +} + +void MigrationExecutor::onStoppedProcessing() +{ + mAlreadyProcessed++; + Q_ASSERT(mTotalAmount > 0); + //TODO: setProcessedAmount would be better, but we need support for suitable units first (there's only files, folders, bytes). + setPercent(mAlreadyProcessed * 100.0 / mTotalAmount); + mCurrentMigrator.clear(); + executeNext(); +} + +bool MigrationExecutor::doSuspend() +{ + if (mCurrentMigrator) { + QSharedPointer migrator = mCurrentMigrator.toStrongRef(); + if (migrator) { + migrator->pause(); + } else { + mCurrentMigrator.clear(); + } + } + Q_EMIT infoMessage(this, i18nc("PIM-Maintenance is paused.", "Paused.")); + mSuspended = true; + return true; +} + +bool MigrationExecutor::doResume() +{ + mSuspended = false; + if (mCurrentMigrator) { + QSharedPointer migrator = mCurrentMigrator.toStrongRef(); + if (migrator) { + migrator->resume(); + } else { + mCurrentMigrator.clear(); + } + } + executeNext(); + return true; +} + diff --git a/agents/migration/migrationexecutor.h b/agents/migration/migrationexecutor.h new file mode 100644 index 00000000..2e50e893 --- /dev/null +++ b/agents/migration/migrationexecutor.h @@ -0,0 +1,61 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MIGRATIONEXECUTOR_H +#define MIGRATIONEXECUTOR_H + +#include +#include +#include +#include + +/** + * An executor can contain multiple jobs that are scheduled by the executor. + * + * The executor is responsible for starting/pausing/stopping the individual migrators. + * + * This job is used to give overall progress information and start/stop controls to KUIServer via KUiServerJobTracker. + */ +class MigrationExecutor : public KJob +{ + Q_OBJECT +public: + MigrationExecutor(); + void add(const QSharedPointer &); + void start() Q_DECL_OVERRIDE; + +protected: + bool doResume() Q_DECL_OVERRIDE; + bool doSuspend() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onStoppedProcessing(); + void executeNext(); + +private: + QQueue< QWeakPointer > mQueue; + QWeakPointer mCurrentMigrator; + bool mSuspended; + int mTotalAmount; + int mAlreadyProcessed; +}; + +#endif diff --git a/agents/migration/migrationscheduler.cpp b/agents/migration/migrationscheduler.cpp new file mode 100644 index 00000000..cf12c17b --- /dev/null +++ b/agents/migration/migrationscheduler.cpp @@ -0,0 +1,298 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * 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 . + * + */ + +#include "migrationscheduler.h" + +#include +#include +#include +#include + +#include "migrationexecutor.h" + +void LogModel::message(MigratorBase::MessageType type, const QString &msg) +{ + switch (type) { + case MigratorBase::Success: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-ok-apply")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Skip: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-ok")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Info: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-information")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Warning: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-warning")), msg); + item->setEditable(false); + appendRow(item); + break; + } + case MigratorBase::Error: { + QStandardItem *item = new QStandardItem(QIcon::fromTheme(QStringLiteral("dialog-error")), msg); + item->setEditable(false); + appendRow(item); + break; + } + default: + qCritical() << "unknown type " << type; + } +} + +Row::Row(const QSharedPointer &migrator, MigratorModel &model) + : QObject(), + mMigrator(migrator), + mModel(model) +{ + connect(migrator.data(), &MigratorBase::stateChanged, this, &Row::stateChanged); + connect(migrator.data(), SIGNAL(progress(int)), this, SLOT(progress(int))); +} + +bool Row::operator==(const Row &other) const +{ + return mMigrator->identifier() == other.mMigrator->identifier(); +} + +void Row::stateChanged(MigratorBase::MigrationState /*newState*/) +{ + mModel.columnChanged(*this, MigratorModel::State); +} + +void Row::progress(int /*prog*/) +{ + mModel.columnChanged(*this, MigratorModel::Progress); +} + +int MigratorModel::positionOf(const Row &row) +{ + int pos = 0; + foreach (const QSharedPointer &r, mMigrators) { + if (row == *r) { + return pos; + } + pos++; + } + return -1; +} + +void MigratorModel::columnChanged(const Row &row, int col) +{ + const int p = positionOf(row); + Q_ASSERT(p >= 0); + if (p >= 0) { + const QModelIndex idx = index(p, col); + Q_EMIT dataChanged(idx, idx); + } +} + +bool MigratorModel::addMigrator(const QSharedPointer &m) +{ + if (migrator(m->identifier())) { + qWarning() << "Model already contains a migrator with the identifier: " << m; + return false; + } + const int pos = mMigrators.size(); + beginInsertRows(QModelIndex(), pos, pos); + mMigrators.append(QSharedPointer(new Row(m, *this))); + endInsertRows(); + return true; +} + +int MigratorModel::columnCount(const QModelIndex &/*parent*/) const +{ + return ColumnCount; +} + +int MigratorModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return mMigrators.size(); + } + return 0; +} + +QModelIndex MigratorModel::index(int row, int column, const QModelIndex &parent) const +{ + if (row >= rowCount(parent) || row < 0) { + return QModelIndex(); + } + return createIndex(row, column, static_cast(mMigrators.at(row).data())); +} + +QModelIndex MigratorModel::parent(const QModelIndex &/*child*/) const +{ + return QModelIndex(); +} + +QVariant MigratorModel::headerData(int section, Qt::Orientation /*orientation*/, int role) const +{ + if (role == Qt::DisplayRole) { + switch (section) { + case Name: + return i18nc("Name of the migrator in this row", "Name"); + case Progress: + return i18nc("Progress of the mgirator in %", "Progress"); + case State: + return i18nc("Current status of the migrator (done, in progress, ...)", "Status"); + default: + Q_ASSERT(false); + } + } + return QVariant(); +} + +QVariant MigratorModel::data(const QModelIndex &index, int role) const +{ + const Row *row = static_cast(index.internalPointer()); + const QSharedPointer migrator(row->mMigrator); + if (!migrator) { + qWarning() << "migrator not found"; + return QVariant(); + } + switch (role) { + case Qt::DisplayRole: + switch (index.column()) { + case Name: + return migrator->displayName(); + case Progress: + return QStringLiteral("%1 %").arg(migrator->progress()); + case State: + return migrator->status(); + default: + Q_ASSERT(false); + } + case IdentifierRole: + return migrator->identifier(); + case LogfileRole: + return migrator->logfile(); + case Qt::ToolTipRole: + return migrator->description(); + default: + break; + } + return QVariant(); +} + +QSharedPointer MigratorModel::migrator(const QString &identifier) const +{ + foreach (const QSharedPointer &row, mMigrators) { + if (row->mMigrator->identifier() == identifier) { + return row->mMigrator; + } + } + return QSharedPointer(); +} + +QList< QSharedPointer > MigratorModel::migrators() const +{ + QList< QSharedPointer > migrators; + foreach (const QSharedPointer &row, mMigrators) { + return migrators << row->mMigrator; + } + return migrators; +} + +MigrationScheduler::MigrationScheduler(KJobTrackerInterface *jobTracker, QObject *parent) + : QObject(parent), + mModel(new MigratorModel), + mJobTracker(jobTracker) +{ +} + +MigrationScheduler::~MigrationScheduler() +{ + delete mAutostartExecutor; +} + +void MigrationScheduler::addMigrator(const QSharedPointer &migrator) +{ + if (mModel->addMigrator(migrator)) { + QSharedPointer logModel(new LogModel); + connect(migrator.data(), &MigratorBase::message, logModel.data(), &LogModel::message); + mLogModel.insert(migrator->identifier(), logModel); + if (migrator->shouldAutostart()) { + checkForAutostart(migrator); + } + } +} + +QAbstractItemModel &MigrationScheduler::model() +{ + return *mModel; +} + +QStandardItemModel &MigrationScheduler::logModel(const QString &identifier) +{ + Q_ASSERT(mLogModel.contains(identifier)); + return *mLogModel.value(identifier); +} + +void MigrationScheduler::checkForAutostart(const QSharedPointer &migrator) +{ + if (migrator->migrationState() != MigratorBase::Complete) { + + if (!mAutostartExecutor) { + mAutostartExecutor = new MigrationExecutor; + if (mJobTracker) { + mJobTracker->registerJob(mAutostartExecutor); + } + + mAutostartExecutor->start(); + } + + mAutostartExecutor->add(migrator); + } +} + +void MigrationScheduler::start(const QString &identifier) +{ + //TODO create separate executor? + const QSharedPointer m = mModel->migrator(identifier); + if (m) { + m->start(); + } +} + +void MigrationScheduler::pause(const QString &identifier) +{ + const QSharedPointer m = mModel->migrator(identifier); + if (m) { + m->pause(); + } +} + +void MigrationScheduler::abort(const QString &identifier) +{ + const QSharedPointer m = mModel->migrator(identifier); + if (m) { + m->abort(); + } +} + diff --git a/agents/migration/migrationscheduler.h b/agents/migration/migrationscheduler.h new file mode 100644 index 00000000..8823b8af --- /dev/null +++ b/agents/migration/migrationscheduler.h @@ -0,0 +1,132 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MIGRATIONSCHEDULER_H +#define MIGRATIONSCHEDULER_H + +#include "migratorbase.h" +#include +#include +#include +#include +#include + +class MigrationExecutor; +class KJobTrackerInterface; +class MigratorModel; + +class LogModel : public QStandardItemModel +{ + Q_OBJECT +public Q_SLOTS: + void message(MigratorBase::MessageType type, const QString &msg); +}; + +class Row: public QObject +{ + Q_OBJECT +public: + QSharedPointer mMigrator; + MigratorModel &mModel; + + explicit Row(const QSharedPointer &migrator, MigratorModel &model); + + bool operator==(const Row &other) const; + +private Q_SLOTS: + void stateChanged(MigratorBase::MigrationState); + void progress(int); +}; + +/** + * The model serves as container for the migrators and exposes the status of each migrator. + * + * It can be plugged into a Listview to inform about the migration progress. + */ +class MigratorModel: public QAbstractItemModel +{ +public: + enum Roles { + IdentifierRole = Qt::UserRole + 1, + LogfileRole + }; + bool addMigrator(const QSharedPointer &migrator); + + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex &child) const Q_DECL_OVERRIDE; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + + QSharedPointer migrator(const QString &identifier) const; + QList< QSharedPointer > migrators() const; + +private: + enum Columns { + Name = 0, + Progress = 1, + State = 2, + ColumnCount + }; + friend class Row; + int positionOf(const Row &); + void columnChanged(const Row &, int column); + QList< QSharedPointer > mMigrators; +}; + +/** + * Scheduler for migration jobs. + * + * Status information is exposed via getModel, which returns a list model containing all migrators with basic information. + * Additionally a logmodel is available via getLogModel for each migrator. The logmodel is continuously filled with information, and can be requested and displayed at any time. + * + * Migrators which return true on shouldAutostart() automatically enter a queue to be processed one after the other. + * When manually triggered it is possible though to run multiple jobs in parallel. + */ +class MigrationScheduler : public QObject +{ + Q_OBJECT +public: + explicit MigrationScheduler(KJobTrackerInterface *jobTracker = Q_NULLPTR, QObject *parent = Q_NULLPTR); + virtual ~MigrationScheduler(); + + void addMigrator(const QSharedPointer &migrator); + + //A model for the view + QAbstractItemModel &model(); + QStandardItemModel &logModel(const QString &identifier); + + //Control + void start(const QString &identifier); + void pause(const QString &identifier); + void abort(const QString &identifier); + +private: + void checkForAutostart(const QSharedPointer &migrator); + + QScopedPointer mModel; + QHash > mLogModel; + QPointer mAutostartExecutor; + KJobTrackerInterface *mJobTracker; +}; + +#endif // MIGRATIONSCHEDULER_H diff --git a/agents/migration/migrationstatuswidget.cpp b/agents/migration/migrationstatuswidget.cpp new file mode 100644 index 00000000..6264e009 --- /dev/null +++ b/agents/migration/migrationstatuswidget.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * 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 . + * + */ + +#include "migrationstatuswidget.h" +#include "migrationscheduler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MigrationStatusWidget::MigrationStatusWidget(MigrationScheduler &scheduler, QWidget *parent) + : QWidget(parent), + mScheduler(scheduler) +{ + QVBoxLayout *vboxLayout = new QVBoxLayout; + { + QToolBar *toolbar = new QToolBar(QStringLiteral("MigrationControlToolbar"), this); + + QAction *start = toolbar->addAction(QStringLiteral("Start")); + start->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start"))); + connect(start, &QAction::triggered, this, &MigrationStatusWidget::startSelected); + + QAction *pause = toolbar->addAction(QStringLiteral("Pause")); + pause->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-pause"))); + connect(pause, &QAction::triggered, this, &MigrationStatusWidget::pauseSelected); + + QAction *abort = toolbar->addAction(QStringLiteral("Abort")); + abort->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-stop"))); + connect(abort, &QAction::triggered, this, &MigrationStatusWidget::abortSelected); + + vboxLayout->addWidget(toolbar); + } + { + QTreeView *treeView = new QTreeView(this); + treeView->setModel(&mScheduler.model()); + mSelectionModel = treeView->selectionModel(); + Q_ASSERT(mSelectionModel); + //Not sure why this is required, but otherwise the view doesn't load anything from the model + treeView->update(QModelIndex()); + connect(treeView, &QTreeView::doubleClicked, this, &MigrationStatusWidget::onItemActivated); + + vboxLayout->addWidget(treeView); + } + setLayout(vboxLayout); +} + +void MigrationStatusWidget::startSelected() +{ + foreach (const QModelIndex &index, mSelectionModel->selectedRows()) { + mScheduler.start(index.data(MigratorModel::IdentifierRole).toString()); + } +} + +void MigrationStatusWidget::pauseSelected() +{ + foreach (const QModelIndex &index, mSelectionModel->selectedRows()) { + mScheduler.pause(index.data(MigratorModel::IdentifierRole).toString()); + } +} + +void MigrationStatusWidget::abortSelected() +{ + foreach (const QModelIndex &index, mSelectionModel->selectedRows()) { + mScheduler.abort(index.data(MigratorModel::IdentifierRole).toString()); + } +} + +void MigrationStatusWidget::onItemActivated(const QModelIndex &index) +{ + QDialog *dlg = new QDialog(this); + QVBoxLayout *topLayout = new QVBoxLayout; + dlg->setLayout(topLayout); + QWidget *widget = new QWidget(dlg); + topLayout->addWidget(widget); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox, &QDialogButtonBox::accepted, dlg, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, dlg, &QDialog::reject); + topLayout->addWidget(buttonBox); + + QVBoxLayout *vboxLayout = new QVBoxLayout; + { + QListView *listView = new QListView(widget); + listView->setModel(&mScheduler.logModel(index.data(MigratorModel::IdentifierRole).toString())); + listView->setAutoScroll(true); + listView->scrollToBottom(); + vboxLayout->addWidget(listView); + } + { + QHBoxLayout *hboxLayout = new QHBoxLayout; + QLabel *label = new QLabel(QStringLiteral("%2").arg(index.data(MigratorModel::LogfileRole).toString()).arg(ki18n("Logfile").toString()), widget); + label->setOpenExternalLinks(true); + hboxLayout->addWidget(label); + hboxLayout->addStretch(); + vboxLayout->addLayout(hboxLayout); + } + widget->setLayout(vboxLayout); + + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->setWindowTitle(i18nc("Title of the window displaying the log of a single migration job.", "Migration Info")); + dlg->resize(600, 300); + dlg->show(); +} + diff --git a/agents/migration/migrationstatuswidget.h b/agents/migration/migrationstatuswidget.h new file mode 100644 index 00000000..dec5b9e7 --- /dev/null +++ b/agents/migration/migrationstatuswidget.h @@ -0,0 +1,46 @@ +/* + * Copyright 2013 Christian Mollekopf + * + * 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) 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 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef MIGRATIONSTATUSWIDGET_H +#define MIGRATIONSTATUSWIDGET_H + +#include "migrationscheduler.h" +#include +#include + +class MigrationStatusWidget: public QWidget +{ + Q_OBJECT +public: + explicit MigrationStatusWidget(MigrationScheduler &scheduler, QWidget *parent = Q_NULLPTR); +private Q_SLOTS: + void startSelected(); + void pauseSelected(); + void abortSelected(); +private: + MigrationScheduler &mScheduler; + QItemSelectionModel *mSelectionModel; +public Q_SLOTS: + void onItemActivated(const QModelIndex &); +}; + +#endif // MIGRATIONCONFIGDIALOG_H + diff --git a/agents/newmailnotifier/CMakeLists.txt b/agents/newmailnotifier/CMakeLists.txt new file mode 100644 index 00000000..12fad7bc --- /dev/null +++ b/agents/newmailnotifier/CMakeLists.txt @@ -0,0 +1,63 @@ + +include_directories(${kdepim-runtime_BINARY_DIR}) +add_definitions(-DTRANSLATION_DOMAIN=\"akonadi_newmailnotifier_agent\") + +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +set(newmailnotifieragent_SRCS + newmailnotifiersettingsdialog.cpp + newmailnotifieragent.cpp + specialnotifierjob.cpp + newmailnotifierselectcollectionwidget.cpp + newmailnotifiershowmessagejob.cpp +) + +ecm_qt_declare_logging_category(newmailnotifieragent_SRCS HEADER newmailnotifier_debug.h IDENTIFIER NEWMAILNOTIFIER_LOG CATEGORY_NAME log_newmailnotifier) + +kconfig_add_kcfg_files(newmailnotifieragent_SRCS + newmailnotifieragentsettings.kcfgc + ) + + +qt5_add_dbus_adaptor(newmailnotifieragent_SRCS org.freedesktop.Akonadi.NewMailNotifier.xml newmailnotifieragent.h NewMailNotifierAgent) + + +add_executable( akonadi_newmailnotifier_agent ${newmailnotifieragent_SRCS}) + + +target_link_libraries( akonadi_newmailnotifier_agent + KF5::AkonadiCore + KF5::Mime + KF5::AkonadiMime + KF5::AkonadiContact + KF5::Codecs + KF5::IdentityManagement + KF5::NotifyConfig + KF5::AkonadiAgentBase + KF5::DBusAddons + KF5::XmlGui + KF5::Notifications + KF5::WindowSystem + KF5::Completion + KF5::Service + KF5::IconThemes +) + +if (Qt5TextToSpeech_FOUND) + target_link_libraries(akonadi_newmailnotifier_agent + Qt5::TextToSpeech) +endif() + +if( APPLE ) + set_target_properties( akonadi_newmailnotifier_agent PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/Info.plist.template) + set_target_properties( akonadi_newmailnotifier_agent PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.kde.Akonadi.newmailnotifier") + set_target_properties( akonadi_newmailnotifier_agent PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "KDE New Mail Notifier") +endif () + +install(TARGETS akonadi_newmailnotifier_agent ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) + + +install(FILES newmailnotifieragent.desktop DESTINATION "${KDE_INSTALL_DATAROOTDIR}/akonadi/agents") +install(FILES akonadi_newmailnotifier_agent.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} ) + diff --git a/agents/newmailnotifier/Messages.sh b/agents/newmailnotifier/Messages.sh new file mode 100755 index 00000000..9d815ddb --- /dev/null +++ b/agents/newmailnotifier/Messages.sh @@ -0,0 +1,4 @@ +#! /bin/sh +$EXTRACTRC `find . -name '*.kcfg'` >> rc.cpp || exit 11 +$XGETTEXT *.cpp -o $podir/akonadi_newmailnotifier_agent.pot +rm -f rc.cpp diff --git a/agents/newmailnotifier/TODO b/agents/newmailnotifier/TODO new file mode 100644 index 00000000..9f04060b --- /dev/null +++ b/agents/newmailnotifier/TODO @@ -0,0 +1,4 @@ +for 4.12: +--------- +- Look at https://github.com/shellscape/Gmail-Notifier-Plus/tree/master/Promotional for idea +- Show if we can display full collection path (for the moment we can't) diff --git a/agents/newmailnotifier/akonadi_newmailnotifier_agent.notifyrc b/agents/newmailnotifier/akonadi_newmailnotifier_agent.notifyrc new file mode 100644 index 00000000..78a96aaf --- /dev/null +++ b/agents/newmailnotifier/akonadi_newmailnotifier_agent.notifyrc @@ -0,0 +1,137 @@ +[Global] +IconName=kmail +Comment=New email notify +Comment[bg]=Уведомяване за нова поща +Comment[bs]=Obavijest o novoj pošti +Comment[ca]=Notificador de correu electrònic nou +Comment[ca@valencia]=Notificador de correu electrònic nou +Comment[cs]=Upozornění na nový e-mail +Comment[da]=Bekendtgørelse af nye e-mails +Comment[de]=E-Mail-Benachrichtigung +Comment[el]=Ειδοποίηση νέας αλληλογραφίας +Comment[en_GB]=New email notify +Comment[es]=Nueva notificación de correo +Comment[et]=Uue kirja teavitaja +Comment[fi]=Ilmoitus uudesta postista +Comment[fr]=Notification de nouveaux courriers électroniques +Comment[ga]=Fógairt ríomhphoist nua +Comment[gl]=Notificación de nova mensaxe. +Comment[hu]=Új e-mail értesítés +Comment[ia]=Notifica de nove messages de e-posta +Comment[it]=Notifica dei nuovi messaggi di posta +Comment[kk]=Жаңа эл.пошта туралы хабарлау +Comment[km]=ជូនដំណឹង​អ៊ីមែល​ថ្មី +Comment[ko]=새 메일 알림 +Comment[lt]=Naujo pašto pranešimas +Comment[lv]=Jauna pasta paziņojums +Comment[nb]=Varsling om ny e-post +Comment[nds]=Bescheed över nieg Nettpost +Comment[nl]=Melding van nieuwe e-mail +Comment[pl]=Powiadomienie o nowej poczcie +Comment[pt]=Notificação de correio novo +Comment[pt_BR]=Notificação de novo e-mail +Comment[ru]=Уведомление о новой почте +Comment[sk]=Oznámenie novej pošty +Comment[sl]=Obvestilo o novi e-pošti +Comment[sr]=Обавештење о пристиглој е‑пошти +Comment[sr@ijekavian]=Обавештење о пристиглој е‑пошти +Comment[sr@ijekavianlatin]=Obaveštenje o pristigloj e‑pošti +Comment[sr@latin]=Obaveštenje o pristigloj e‑pošti +Comment[sv]=Ny brevunderrättelse +Comment[tr]=Yeni e-posta bildirimi +Comment[uk]=Сповіщувач про нові повідомлення +Comment[x-test]=xxNew email notifyxx +Comment[zh_CN]=新邮件提醒 +Comment[zh_TW]=新郵件通知 +Name=New email notify +Name[bg]=Уведомяване за нова поща +Name[bs]=Obavijest o novoj pošti +Name[ca]=Notificador de correu electrònic nou +Name[ca@valencia]=Notificador de correu electrònic nou +Name[cs]=Upozornění na nový e-mail +Name[da]=Bekendtgørelse af nye e-mails +Name[de]=E-Mail-Benachrichtigung +Name[el]=Ειδοποίηση νέας αλληλογραφίας +Name[en_GB]=New email notify +Name[es]=Nueva notificación de correo +Name[et]=Uue kirja teavitaja +Name[fi]=Ilmoitus uudesta postista +Name[fr]=Notification de nouveaux courriers électroniques +Name[ga]=Fógairt ríomhphoist nua +Name[gl]=Notificación de nova mensaxe +Name[hu]=Új e-mail értesítés +Name[ia]=Notifica de nove messages de e-posta +Name[it]=Notifica dei nuovi messaggi di posta +Name[kk]=Жаңа эл.пошта туралы хабарлау +Name[km]=ជូនដំណឹង​អ៊ីមែល​ថ្មី +Name[ko]=새 메일 알림 +Name[lt]=Naujo pašto pranešimas +Name[lv]=Jauna pasta paziņojums +Name[nb]=Varsling om ny e-post +Name[nds]=Nieg Nettpost +Name[nl]=Melding van nieuwe e-mail +Name[pl]=Powiadomienie o nowej poczcie +Name[pt]=Notificação de correio novo +Name[pt_BR]=Notificação de novo e-mail +Name[ro]=Notificare mesaje noi +Name[ru]=Уведомление о новой почте +Name[sk]=Oznámenie novej pošty +Name[sl]=Obvestilo o novi e-pošti +Name[sr]=Обавештење о пристиглој е‑пошти +Name[sr@ijekavian]=Обавештење о пристиглој е‑пошти +Name[sr@ijekavianlatin]=Obaveštenje o pristigloj e‑pošti +Name[sr@latin]=Obaveštenje o pristigloj e‑pošti +Name[sv]=Ny brevunderrättelse +Name[tr]=Yeni e-posta bildirimi +Name[uk]=Сповіщувач про нові повідомлення +Name[x-test]=xxNew email notifyxx +Name[zh_CN]=新邮件提醒 +Name[zh_TW]=新郵件通知 + +[Event/new-email] +Name=New email arrived +Name[bg]=Пристигна нова поща +Name[bs]=Nova pošta stigla +Name[ca]=Ha arribat correu electrònic nou +Name[ca@valencia]=Ha arribat correu electrònic nou +Name[cs]=Přišel nový e-mail +Name[da]=Ny e-mail ankommet +Name[de]=Neue Nachrichten sind eingetroffen +Name[el]=Ελήφθη νέα αλληλογραφία +Name[en_GB]=New email arrived +Name[es]=Llegó correo nuevo +Name[et]=Saabus uus kiri +Name[fi]=Uutta postia saapunut +Name[fr]=Un nouveau courrier électronique est arrivé +Name[ga]=Tháinig ríomhphost nua +Name[gl]=Chegou unha mensaxe nova +Name[hu]=Új e-mail érkezett +Name[ia]=Nove message de e-posta arrivava +Name[it]=Nuova posta ricevuta +Name[kk]=Жаңа пошта келді +Name[km]=មាន​អ៊ីមែល​ថ្មី​មក​ដល់ +Name[ko]=새 메일이 도착했습니다 +Name[lt]=Naujas laiškas gautas +Name[lv]=Saņemts jauns e-pasts +Name[nb]=Ny e-post ankommet +Name[nds]=Niege Nettpost ankamen +Name[nl]=Nieuwe e-mail aangekomen +Name[pl]=Nadeszła nowa poczta +Name[pt]=Notificação de correio novo +Name[pt_BR]=Notificação de novo e-mail +Name[ro]=Mesaje noi sosite +Name[ru]=Получена новая почта +Name[sk]=Prišla nová pošta +Name[sl]=Prispela je nova e-pošta +Name[sr]=Стигла је нова е‑пошта +Name[sr@ijekavian]=Стигла је нова е‑пошта +Name[sr@ijekavianlatin]=Stigla je nova e‑pošta +Name[sr@latin]=Stigla je nova e‑pošta +Name[sv]=Ny post har anlänt +Name[tr]=Yeni e-posta alındı +Name[uk]=Надійшла нова пошта +Name[x-test]=xxNew email arrivedxx +Name[zh_CN]=新邮件到达 +Name[zh_TW]=新郵件已抵達 +Action=Popup + diff --git a/agents/newmailnotifier/newmailnotifieragent.cpp b/agents/newmailnotifier/newmailnotifieragent.cpp new file mode 100644 index 00000000..b164e811 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragent.cpp @@ -0,0 +1,575 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + Copyright (c) 2010 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "newmailnotifieragent.h" + +#include +#include "specialnotifierjob.h" +#include "newmailnotifieradaptor.h" +#include "newmailnotifieragentsettings.h" +#include "newmailnotifiersettingsdialog.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "newmailnotifier_debug.h" +#include +#include +#include + +using namespace Akonadi; + +NewMailNotifierAgent::NewMailNotifierAgent(const QString &id) + : AgentBase(id) +{ + Kdelibs4ConfigMigrator migrate(QStringLiteral("newmailnotifieragent")); + migrate.setConfigFiles(QStringList() << QStringLiteral("akonadi_newmailnotifier_agentrc") << QStringLiteral("akonadi_newmailnotifier_agent.notifyrc")); + migrate.migrate(); + + KLocalizedString::setApplicationDomain("akonadi_newmailnotifier_agent"); + Akonadi::AttributeFactory::registerAttribute(); + new NewMailNotifierAdaptor(this); + + mIdentityManager = new KIdentityManagement::IdentityManager(false, this); + connect(mIdentityManager, SIGNAL(changed()), SLOT(slotIdentitiesChanged())); + slotIdentitiesChanged(); + mDefaultPixmap = QIcon::fromTheme(QStringLiteral("kmail")).pixmap(KIconLoader::SizeMedium, KIconLoader::SizeMedium); + + KDBusConnectionPool::threadConnection().registerObject(QStringLiteral("/NewMailNotifierAgent"), + this, QDBusConnection::ExportAdaptors); + KDBusConnectionPool::threadConnection().registerService(QStringLiteral("org.freedesktop.Akonadi.NewMailNotifierAgent")); + + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceStatusChanged, this, &NewMailNotifierAgent::slotInstanceStatusChanged); + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceRemoved, this, &NewMailNotifierAgent::slotInstanceRemoved); + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceAdded, this, &NewMailNotifierAgent::slotInstanceAdded); + connect(Akonadi::AgentManager::self(), &Akonadi::AgentManager::instanceNameChanged, this, &NewMailNotifierAgent::slotInstanceNameChanged); + + changeRecorder()->setMimeTypeMonitored(KMime::Message::mimeType()); + changeRecorder()->itemFetchScope().setCacheOnly(true); + changeRecorder()->itemFetchScope().setFetchModificationTime(false); + changeRecorder()->fetchCollection(true); + changeRecorder()->setChangeRecordingEnabled(false); + changeRecorder()->ignoreSession(Akonadi::Session::defaultSession()); + changeRecorder()->collectionFetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All); + changeRecorder()->setCollectionMonitored(Collection::root(), true); + mTimer.setInterval(5 * 1000); + connect(&mTimer, &QTimer::timeout, this, &NewMailNotifierAgent::slotShowNotifications); + + if (isActive()) { + mTimer.setSingleShot(true); + } +} + +void NewMailNotifierAgent::slotIdentitiesChanged() +{ + mListEmails = mIdentityManager->allEmails(); +} + +void NewMailNotifierAgent::doSetOnline(bool online) +{ + if (!online) { + clearAll(); + } +} + +void NewMailNotifierAgent::setExcludeMyselfFromNotification(bool b) +{ + NewMailNotifierAgentSettings::setExcludeEmailsFromMe(b); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::excludeMyselfFromNotification() const +{ + return NewMailNotifierAgentSettings::excludeEmailsFromMe(); +} + +void NewMailNotifierAgent::setShowPhoto(bool show) +{ + NewMailNotifierAgentSettings::setShowPhoto(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showPhoto() const +{ + return NewMailNotifierAgentSettings::showPhoto(); +} + +void NewMailNotifierAgent::setShowFrom(bool show) +{ + NewMailNotifierAgentSettings::setShowFrom(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showFrom() const +{ + return NewMailNotifierAgentSettings::showFrom(); +} + +void NewMailNotifierAgent::setShowSubject(bool show) +{ + NewMailNotifierAgentSettings::setShowSubject(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showSubject() const +{ + return NewMailNotifierAgentSettings::showSubject(); +} + +void NewMailNotifierAgent::setShowFolderName(bool show) +{ + NewMailNotifierAgentSettings::setShowFolder(show); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::showFolderName() const +{ + return NewMailNotifierAgentSettings::showFolder(); +} + +void NewMailNotifierAgent::setEnableAgent(bool enabled) +{ + NewMailNotifierAgentSettings::setEnabled(enabled); + NewMailNotifierAgentSettings::self()->save(); + if (!enabled) { + clearAll(); + } +} + +void NewMailNotifierAgent::setVerboseMailNotification(bool verbose) +{ + NewMailNotifierAgentSettings::setVerboseNotification(verbose); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::verboseMailNotification() const +{ + return NewMailNotifierAgentSettings::verboseNotification(); +} + +void NewMailNotifierAgent::setTextToSpeakEnabled(bool enabled) +{ + NewMailNotifierAgentSettings::setTextToSpeakEnabled(enabled); + NewMailNotifierAgentSettings::self()->save(); +} + +bool NewMailNotifierAgent::textToSpeakEnabled() const +{ + return NewMailNotifierAgentSettings::textToSpeakEnabled(); +} + +void NewMailNotifierAgent::setTextToSpeak(const QString &msg) +{ + NewMailNotifierAgentSettings::setTextToSpeak(msg); + NewMailNotifierAgentSettings::self()->save(); +} + +QString NewMailNotifierAgent::textToSpeak() const +{ + return NewMailNotifierAgentSettings::textToSpeak(); +} + +void NewMailNotifierAgent::clearAll() +{ + mNewMails.clear(); + mInstanceNameInProgress.clear(); +} + +bool NewMailNotifierAgent::enabledAgent() const +{ + return NewMailNotifierAgentSettings::enabled(); +} + +bool NewMailNotifierAgent::showButtonToDisplayMail() const +{ + return NewMailNotifierAgentSettings::showButtonToDisplayMail(); +} + +void NewMailNotifierAgent::setShowButtonToDisplayMail(bool b) +{ + NewMailNotifierAgentSettings::setShowButtonToDisplayMail(b); + NewMailNotifierAgentSettings::self()->save(); +} + +void NewMailNotifierAgent::showConfigureDialog(qlonglong windowId) +{ + configure(windowId); +} + +void NewMailNotifierAgent::configure(WId windowId) +{ + QPointer dialog = new NewMailNotifierSettingsDialog; + if (windowId) { +#ifndef Q_OS_WIN + KWindowSystem::setMainWindow(dialog, windowId); +#else + KWindowSystem::setMainWindow(dialog, (HWND)windowId); +#endif + } + dialog->exec(); + delete dialog; +} + +bool NewMailNotifierAgent::excludeSpecialCollection(const Akonadi::Collection &collection) const +{ + if (collection.hasAttribute()) { + return true; + } + + if (collection.hasAttribute()) { + if (collection.attribute()->ignoreNewMail()) { + return true; + } + } + + if (!collection.contentMimeTypes().contains(KMime::Message::mimeType())) { + return true; + } + + SpecialMailCollections::Type type = SpecialMailCollections::self()->specialCollectionType(collection); + switch (type) { + case SpecialMailCollections::Invalid: //Not a special collection + case SpecialMailCollections::Inbox: + return false; + default: + return true; + } + +} + +void NewMailNotifierAgent::itemsRemoved(const Item::List &items) +{ + if (!isActive()) { + return; + } + + QHash< Akonadi::Collection, QList >::iterator end(mNewMails.end()); + for (QHash< Akonadi::Collection, QList >::iterator it = mNewMails.begin(); it != end; ++it) { + QList idList = it.value(); + bool itemFound = false; + Q_FOREACH (const Item &item, items) { + if (idList.contains(item.id())) { + idList.removeAll(item.id()); + itemFound = true; + } + } + if (itemFound) { + if (mNewMails[it.key()].isEmpty()) { + mNewMails.remove(it.key()); + } else { + mNewMails[it.key()] = idList; + } + } + } +} + +void NewMailNotifierAgent::itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) +{ + if (!isActive()) { + return; + } + Q_FOREACH (const Akonadi::Item &item, items) { + QHash< Akonadi::Collection, QList >::iterator end(mNewMails.end()); + for (QHash< Akonadi::Collection, QList >::iterator it = mNewMails.begin(); it != end; ++it) { + QList idList = it.value(); + if (idList.contains(item.id()) && addedFlags.contains("\\SEEN")) { + idList.removeAll(item.id()); + if (idList.isEmpty()) { + mNewMails.remove(it.key()); + break; + } else { + (*it) = idList; + } + } + } + } +} + +void NewMailNotifierAgent::itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &collectionSource, const Akonadi::Collection &collectionDestination) +{ + if (!isActive()) { + return; + } + + Q_FOREACH (const Akonadi::Item &item, items) { + if (ignoreStatusMail(item)) { + continue; + } + + if (excludeSpecialCollection(collectionSource)) { + continue; // outbox, sent-mail, trash, drafts or templates. + } + + if (mNewMails.contains(collectionSource)) { + QList idListFrom = mNewMails[ collectionSource ]; + if (idListFrom.contains(item.id())) { + idListFrom.removeAll(item.id()); + + if (idListFrom.isEmpty()) { + mNewMails.remove(collectionSource); + } else { + mNewMails[ collectionSource ] = idListFrom; + } + if (!excludeSpecialCollection(collectionDestination)) { + QList idListTo = mNewMails[ collectionDestination ]; + idListTo.append(item.id()); + mNewMails[ collectionDestination ] = idListTo; + } + } + } + } +} + +bool NewMailNotifierAgent::ignoreStatusMail(const Akonadi::Item &item) +{ + Akonadi::MessageStatus status; + status.setStatusFromFlags(item.flags()); + if (status.isRead() || status.isSpam() || status.isIgnored()) { + return true; + } + return false; +} + +void NewMailNotifierAgent::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) +{ + if (!isActive()) { + return; + } + + if (excludeSpecialCollection(collection)) { + return; // outbox, sent-mail, trash, drafts or templates. + } + + if (ignoreStatusMail(item)) { + return; + } + + if (!mTimer.isActive()) { + mTimer.start(); + } + mNewMails[ collection ].append(item.id()); +} + +void NewMailNotifierAgent::slotShowNotifications() +{ + if (mNewMails.isEmpty()) { + return; + } + + if (!isActive()) { + return; + } + + if (!mInstanceNameInProgress.isEmpty()) { + //Restart timer until all is done. + mTimer.start(); + return; + } + + QString message; + if (NewMailNotifierAgentSettings::verboseNotification()) { + bool hasUniqMessage = true; + Akonadi::Item::Id item = -1; + QString currentPath; + QStringList texts; + QHash< Akonadi::Collection, QList >::const_iterator end(mNewMails.constEnd()); + const int numberOfCollection(mNewMails.count()); + if (numberOfCollection > 1) { + hasUniqMessage = false; + } + + for (QHash< Akonadi::Collection, QList >::const_iterator it = mNewMails.constBegin(); it != end; ++it) { + Akonadi::EntityDisplayAttribute *attr = it.key().attribute(); + QString displayName; + if (attr && !attr->displayName().isEmpty()) { + displayName = attr->displayName(); + } else { + displayName = it.key().name(); + } + + if (hasUniqMessage) { + if (it.value().count() == 0) { + //You can have an unique folder with 0 message + return; + } else if (it.value().count() == 1) { + item = it.value().at(0); + currentPath = displayName; + break; + } else { + hasUniqMessage = false; + } + } + QString resourceName; + if (!mCacheResourceName.contains(it.key().resource())) { + Q_FOREACH (const Akonadi::AgentInstance &instance, Akonadi::AgentManager::self()->instances()) { + if (instance.identifier() == it.key().resource()) { + mCacheResourceName.insert(instance.identifier(), instance.name()); + resourceName = instance.name(); + break; + } + } + } else { + resourceName = mCacheResourceName.value(it.key().resource()); + } + const int numberOfEmails(it.value().count()); + if (numberOfEmails > 0) { + texts.append(i18ncp("%2 = name of mail folder; %3 = name of Akonadi POP3/IMAP/etc resource (as user named it)", + "One new email in %2 from \"%3\"", + "%1 new emails in %2 from \"%3\"", numberOfEmails, displayName, + resourceName)); + } + } + if (hasUniqMessage) { + SpecialNotifierJob *job = new SpecialNotifierJob(mListEmails, currentPath, item, this); + job->setDefaultPixmap(mDefaultPixmap); + connect(job, &SpecialNotifierJob::displayNotification, this, &NewMailNotifierAgent::slotDisplayNotification); + + mNewMails.clear(); + return; + } else { + message = texts.join(QStringLiteral("
")); + } + } else { + message = i18n("New mail arrived"); + } + + qCDebug(NEWMAILNOTIFIER_LOG) << message; + + slotDisplayNotification(mDefaultPixmap, message); + + mNewMails.clear(); +} + +void NewMailNotifierAgent::slotDisplayNotification(const QPixmap &pixmap, const QString &message) +{ + KNotification::event(QStringLiteral("new-email"), + message, + pixmap, + Q_NULLPTR, + KNotification::CloseOnTimeout, + QStringLiteral("akonadi_newmailnotifier_agent")); + +} + +void NewMailNotifierAgent::slotInstanceNameChanged(const Akonadi::AgentInstance &instance) +{ + if (!isActive()) { + return; + } + + const QString identifier(instance.identifier()); + if (mCacheResourceName.contains(identifier)) { + mCacheResourceName.remove(identifier); + mCacheResourceName.insert(identifier, instance.name()); + } +} + +void NewMailNotifierAgent::slotInstanceStatusChanged(const Akonadi::AgentInstance &instance) +{ + if (!isActive()) { + return; + } + + const QString identifier(instance.identifier()); + switch (instance.status()) { + case Akonadi::AgentInstance::Broken: + case Akonadi::AgentInstance::Idle: { + if (mInstanceNameInProgress.contains(identifier)) { + mInstanceNameInProgress.removeAll(identifier); + } + break; + } + case Akonadi::AgentInstance::Running: { + if (!excludeAgentType(instance)) { + if (!mInstanceNameInProgress.contains(identifier)) { + mInstanceNameInProgress.append(identifier); + } + } + break; + } + case Akonadi::AgentInstance::NotConfigured: + //Nothing + break; + } +} + +bool NewMailNotifierAgent::excludeAgentType(const Akonadi::AgentInstance &instance) +{ + if (instance.type().mimeTypes().contains(KMime::Message::mimeType())) { + const QStringList capabilities(instance.type().capabilities()); + if (capabilities.contains(QStringLiteral("Resource")) && + !capabilities.contains(QStringLiteral("Virtual")) && + !capabilities.contains(QStringLiteral("MailTransport"))) { + return false; + } else { + return true; + } + } + return true; +} + +void NewMailNotifierAgent::slotInstanceRemoved(const Akonadi::AgentInstance &instance) +{ + if (!isActive()) { + return; + } + + const QString identifier(instance.identifier()); + if (mInstanceNameInProgress.contains(identifier)) { + mInstanceNameInProgress.removeAll(identifier); + } +} + +void NewMailNotifierAgent::slotInstanceAdded(const Akonadi::AgentInstance &instance) +{ + mCacheResourceName.insert(instance.identifier(), instance.name()); +} + +void NewMailNotifierAgent::printDebug() +{ + qCDebug(NEWMAILNOTIFIER_LOG) << "instance in progress: " << mInstanceNameInProgress + << "\n notifier enabled : " << NewMailNotifierAgentSettings::enabled() + << "\n check in progress : " << !mInstanceNameInProgress.isEmpty(); +} + +bool NewMailNotifierAgent::isActive() const +{ + return isOnline() && NewMailNotifierAgentSettings::enabled(); +} + +AKONADI_AGENT_MAIN(NewMailNotifierAgent) + diff --git a/agents/newmailnotifier/newmailnotifieragent.desktop b/agents/newmailnotifier/newmailnotifieragent.desktop new file mode 100644 index 00000000..9bffe6b5 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragent.desktop @@ -0,0 +1,97 @@ +[Desktop Entry] +Name=New Email Notifier +Name[bg]=Уведомяване за нова поща +Name[bs]=Novi obavještavač o pošti +Name[ca]=Notificador de correu electrònic nou +Name[ca@valencia]=Notificador de correu electrònic nou +Name[cs]=Upozornění na nový e-mail +Name[da]=Bekendtgørelse af nye e-mails +Name[de]=E-Mail-Benachrichtigung +Name[el]=Ειδοποιητής νέας αλληλογραφίας +Name[en_GB]=New Email Notifier +Name[es]=Nuevo notificador de correo +Name[et]=Uue kirja teavitaja +Name[fi]=Uudesta postista ilmoitin +Name[fr]=Notification de nouveaux courriers électroniques +Name[ga]=Fógra Ríomhphoist Nua +Name[gl]=Novo notificador de correo electrónico +Name[hu]=Új e-mail értesítő +Name[ia]=Notificator de nove messages de e-posta +Name[it]=Notifiche dei nuovi messaggi di posta +Name[kk]=Жаңа эл.пошта туралы хабарлау +Name[km]=កម្មវិធី​ជូន​ដំណឹង​អ៊ីមែល​ថ្មី +Name[ko]=새 메일 알리미 +Name[lt]=Naujo pašto pranešėjas +Name[lv]=Jauna pasta paziņotājs +Name[nb]=Varsling om ny e-post +Name[nds]=Nieg-Nettpost-Bescheedgever +Name[nl]=Nieuwe e-mailmelder +Name[pa]=ਨਵੀਂ ਈਮੇਲ ਸੂਚਨਾ +Name[pl]=Powiadomienie o nowej poczcie +Name[pt]=Notificação de Correio Novo +Name[pt_BR]=Notificação de novo e-mail +Name[ro]=Notificator mesaje noi +Name[ru]=Уведомления о новой почте +Name[sk]=Oznamovač novej pošty +Name[sl]=Obvestilnik o novi e-pošti +Name[sr]=Извештавач о пристиглој е‑пошти +Name[sr@ijekavian]=Извештавач о пристиглој е‑пошти +Name[sr@ijekavianlatin]=Izveštavač o pristigloj e‑pošti +Name[sr@latin]=Izveštavač o pristigloj e‑pošti +Name[sv]=Ny brevunderrättelse +Name[tr]=Yeni E-posta Bildirimi +Name[uk]=Сповіщувач про нові повідомлення +Name[x-test]=xxNew Email Notifierxx +Name[zh_CN]=新邮件提醒 +Name[zh_TW]=新郵件通知器 +Comment=Notifications about newly received emails +Comment[ast]=Avisos tocante a correos nuevos recibíos +Comment[bs]=Obavještenja o novoprimljenoj pošti +Comment[ca]=Notificacions quant a correus electrònics nous rebuts +Comment[ca@valencia]=Notificacions quant a correus electrònics nous rebuts +Comment[cs]=Oznamování nově příchozích e-mailů +Comment[da]=Bekendtgørelser om nyligt modtagne e-mails +Comment[de]=Benachrichtigungen über neu empfangene E-Mails +Comment[el]=Ειδοποιήσεις για νέα αλληλογραφία +Comment[en_GB]=Notifications about newly received emails +Comment[es]=Notificaciones sobre correos recibidos recientemente +Comment[et]=Märguanded äsja saabunud e-kirjade kohta +Comment[fi]=Ilmoitukset saapuneesta uudesta sähköpostista +Comment[fr]=Notifications à propos des courriers électroniques récemment reçus +Comment[ga]=Fógraí faoi ríomhphost nua +Comment[gl]=Notificacións de mensaxes acabadas de chegar +Comment[hu]=Értesítések újonnan érkezett e-mailekről +Comment[ia]=Notificationes re nove e-postas recipite +Comment[it]=Notifiche dei messaggi di posta elettronica ricevuti recentemente +Comment[kk]=Жаңа эл.пошта келгені туралы хабарлау +Comment[km]=ការ​ជូន​ដំណឹង​អំពី​អ៊ីមែល​​​ដែល​បាន​ទទួល​ថ្មីៗ +Comment[ko]=새로 받은 메일 알림 +Comment[lt]=Pranešimai apie naujai atsiųstus el. laiškus +Comment[lv]=Paziņojumi par jauniem e-pastiem +Comment[nb]=Varslinger om nylig mottatte e-poster +Comment[nds]=Bescheden över nieg rinkamen Nettbreven +Comment[nl]=Meldingen over nieuw ontvangen e-mails +Comment[pl]=Powiadomienia o nowo otrzymanych wiadomościach +Comment[pt]=Notificações acerca do correio novo recebido +Comment[pt_BR]=Notificações sobre os novos e-mails recebidos +Comment[ro]=Notificări despre mesajele noi +Comment[ru]=Уведомления о получении новых электронных писем +Comment[sk]=Notifikácie o novo prijatých e-mailoch +Comment[sl]=Obvestila o na novo prispeli e-pošti +Comment[sr]=Обавештења о недавно пристиглој е‑пошти +Comment[sr@ijekavian]=Обавештења о недавно пристиглој е‑пошти +Comment[sr@ijekavianlatin]=Obaveštenja o nedavno pristigloj e‑pošti +Comment[sr@latin]=Obaveštenja o nedavno pristigloj e‑pošti +Comment[sv]=Underrättelser om nyss mottagen e-post +Comment[tr]=Yeni gelen e-postalar için bildirimler +Comment[uk]=Сповіщення щодо щойно отриманих повідомлень +Comment[x-test]=xxNotifications about newly received emailsxx +Comment[zh_CN]=新邮件通知 +Comment[zh_TW]=收到新郵件的通知 +Icon=mail-unread-new +Type=AkonadiAgent +Exec=akonadi_newmailnotifier_agent + +X-Akonadi-MimeTypes=message/rfc822 +X-Akonadi-Capabilities=Unique,Autostart +X-Akonadi-Identifier=akonadi_newmailnotifier_agent diff --git a/agents/newmailnotifier/newmailnotifieragent.h b/agents/newmailnotifier/newmailnotifieragent.h new file mode 100644 index 00000000..fe0f152a --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragent.h @@ -0,0 +1,117 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + Copyright (c) 2010 Volker Krause + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef NEWMAILNOTIFIERAGENT_H +#define NEWMAILNOTIFIERAGENT_H + +#include // make sure this is included before QHash, otherwise it wont find the correct qHash implementation for some reason +#include + +#include +#include +#include +namespace Akonadi +{ +class AgentInstance; +} + +namespace KIdentityManagement +{ +class IdentityManager; +} + +class NewMailNotifierAgent : public Akonadi::AgentBase, public Akonadi::AgentBase::ObserverV3 +{ + Q_OBJECT + +public: + explicit NewMailNotifierAgent(const QString &id); + + void showConfigureDialog(qlonglong windowId = 0); + + void setEnableAgent(bool b); + bool enabledAgent() const; + + void setVerboseMailNotification(bool b); + bool verboseMailNotification() const; + + void setBeepOnNewMails(bool b); + bool beepOnNewMails() const; + + void setShowPhoto(bool b); + bool showPhoto() const; + + void setShowFrom(bool b); + bool showFrom() const; + + void setShowSubject(bool b); + bool showSubject() const; + + void setShowFolderName(bool b); + bool showFolderName() const; + + void setExcludeMyselfFromNotification(bool b); + bool excludeMyselfFromNotification() const; + + void setTextToSpeakEnabled(bool enabled); + bool textToSpeakEnabled() const; + + QString textToSpeak() const; + void setTextToSpeak(const QString &msg); + + void printDebug(); + + bool showButtonToDisplayMail() const; + void setShowButtonToDisplayMail(bool b); + +protected: + void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) Q_DECL_OVERRIDE; + void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &sourceCollection, const Akonadi::Collection &destinationCollection) Q_DECL_OVERRIDE; + void itemsRemoved(const Akonadi::Item::List &items) Q_DECL_OVERRIDE; + void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet &addedFlags, const QSet &removedFlags) Q_DECL_OVERRIDE; + void doSetOnline(bool online) Q_DECL_OVERRIDE; + +private Q_SLOTS: + void slotShowNotifications(); + void configure(WId windowId) Q_DECL_OVERRIDE; + void slotInstanceStatusChanged(const Akonadi::AgentInstance &instance); + void slotInstanceRemoved(const Akonadi::AgentInstance &instance); + void slotInstanceAdded(const Akonadi::AgentInstance &instance); + void slotDisplayNotification(const QPixmap &pixmap, const QString &message); + void slotIdentitiesChanged(); + void slotInstanceNameChanged(const Akonadi::AgentInstance &instance); + +private: + bool excludeAgentType(const Akonadi::AgentInstance &instance); + bool ignoreStatusMail(const Akonadi::Item &item); + bool isActive() const; + void clearAll(); + bool excludeSpecialCollection(const Akonadi::Collection &collection) const; + QPixmap mDefaultPixmap; + QStringList mListEmails; + QHash > mNewMails; + QHash mCacheResourceName; + QTimer mTimer; + QStringList mInstanceNameInProgress; + KIdentityManagement::IdentityManager *mIdentityManager; +}; + +#endif diff --git a/agents/newmailnotifier/newmailnotifieragentsettings.kcfg b/agents/newmailnotifier/newmailnotifieragentsettings.kcfg new file mode 100644 index 00000000..7546b571 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragentsettings.kcfg @@ -0,0 +1,40 @@ + + + KLocalizedString + + + true + + + true + + + true + + + true + + + true + + + true + + + false + + + false + + + i18nc("%s is a variable for agent. Do not change it", "A message was received from %s") + + + + false + + + diff --git a/agents/newmailnotifier/newmailnotifieragentsettings.kcfgc b/agents/newmailnotifier/newmailnotifieragentsettings.kcfgc new file mode 100644 index 00000000..e7f02441 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifieragentsettings.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=newmailnotifieragentsettings.kcfg +ClassName=NewMailNotifierAgentSettings +Singleton=true +Mutators=true +SetUserTexts=true diff --git a/agents/newmailnotifier/newmailnotifierselectcollectionwidget.cpp b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.cpp new file mode 100644 index 00000000..13d53785 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.cpp @@ -0,0 +1,218 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "newmailnotifierselectcollectionwidget.h" +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include "newmailnotifier_debug.h" + +#include +#include +#include +#include +#include + +NewMailNotifierSelectCollectionWidget::NewMailNotifierSelectCollectionWidget(QWidget *parent) + : QWidget(parent), + mNeedUpdate(false) +{ + QVBoxLayout *vbox = new QVBoxLayout; + + QLabel *label = new QLabel(i18n("Select which folders to monitor for new message notifications:")); + vbox->addWidget(label); + + // Create a new change recorder. + mChangeRecorder = new Akonadi::ChangeRecorder(this); + mChangeRecorder->setMimeTypeMonitored(KMime::Message::mimeType()); + mChangeRecorder->fetchCollection(true); + mChangeRecorder->setAllMonitored(true); + + mModel = new Akonadi::EntityTreeModel(mChangeRecorder, this); + // Set the model to show only collections, not items. + mModel->setItemPopulationStrategy(Akonadi::EntityTreeModel::NoItemPopulation); + connect(mModel, &Akonadi::EntityTreeModel::collectionTreeFetched, this, &NewMailNotifierSelectCollectionWidget::slotCollectionTreeFetched); + + Akonadi::CollectionFilterProxyModel *mimeTypeProxy = new Akonadi::CollectionFilterProxyModel(this); + mimeTypeProxy->setExcludeVirtualCollections(true); + mimeTypeProxy->addMimeTypeFilters(QStringList() << KMime::Message::mimeType()); + mimeTypeProxy->setSourceModel(mModel); + + // Create the Check proxy model. + mSelectionModel = new QItemSelectionModel(mimeTypeProxy); + mCheckProxy = new KCheckableProxyModel(this); + mCheckProxy->setSelectionModel(mSelectionModel); + mCheckProxy->setSourceModel(mimeTypeProxy); + + mCollectionFilter = new KRecursiveFilterProxyModel(this); + mCollectionFilter->setSourceModel(mCheckProxy); + mCollectionFilter->setDynamicSortFilter(true); + mCollectionFilter->setFilterCaseSensitivity(Qt::CaseInsensitive); + + KLineEdit *searchLine = new KLineEdit(this); + searchLine->setPlaceholderText(i18n("Search...")); + searchLine->setClearButtonShown(true); + connect(searchLine, &QLineEdit::textChanged, + this, &NewMailNotifierSelectCollectionWidget::slotSetCollectionFilter); + + vbox->addWidget(searchLine); + + mFolderView = new QTreeView; + mFolderView->setEditTriggers(QAbstractItemView::NoEditTriggers); + mFolderView->setAlternatingRowColors(true); + vbox->addWidget(mFolderView); + + mFolderView->setModel(mCollectionFilter); + + QHBoxLayout *hbox = new QHBoxLayout; + vbox->addLayout(hbox); + + QPushButton *button = new QPushButton(i18n("&Select All"), this); + connect(button, &QPushButton::clicked, this, &NewMailNotifierSelectCollectionWidget::slotSelectAllCollections); + hbox->addWidget(button); + + button = new QPushButton(i18n("&Unselect All"), this); + connect(button, &QPushButton::clicked, this, &NewMailNotifierSelectCollectionWidget::slotUnselectAllCollections); + hbox->addWidget(button); + hbox->addStretch(1); + setLayout(vbox); +} + +NewMailNotifierSelectCollectionWidget::~NewMailNotifierSelectCollectionWidget() +{ + +} + +void NewMailNotifierSelectCollectionWidget::slotCollectionTreeFetched() +{ + if (!mNeedUpdate) { + mNeedUpdate = true; + QTimer::singleShot(1000, this, &NewMailNotifierSelectCollectionWidget::slotUpdateCollectionStatus); + } + mFolderView->expandAll(); +} + +void NewMailNotifierSelectCollectionWidget::slotSetCollectionFilter(const QString &filter) +{ + mCollectionFilter->setFilterWildcard(filter); + mFolderView->expandAll(); +} + +void NewMailNotifierSelectCollectionWidget::slotUpdateCollectionStatus() +{ + updateStatus(QModelIndex()); +} + +void NewMailNotifierSelectCollectionWidget::slotSelectAllCollections() +{ + forceStatus(QModelIndex(), true); +} + +void NewMailNotifierSelectCollectionWidget::slotUnselectAllCollections() +{ + forceStatus(QModelIndex(), false); +} + +void NewMailNotifierSelectCollectionWidget::updateStatus(const QModelIndex &parent) +{ + const int nbCol = mCheckProxy->rowCount(parent); + for (int i = 0; i < nbCol; ++i) { + const QModelIndex child = mCheckProxy->index(i, 0, parent); + + const Akonadi::Collection collection = + mCheckProxy->data(child, Akonadi::EntityTreeModel::CollectionRole).value(); + + Akonadi::NewMailNotifierAttribute *attr = collection.attribute(); + if (!attr || !attr->ignoreNewMail()) { + mCheckProxy->setData(child, Qt::Checked, Qt::CheckStateRole); + } + updateStatus(child); + } + mNeedUpdate = false; +} + +void NewMailNotifierSelectCollectionWidget::forceStatus(const QModelIndex &parent, bool status) +{ + const int nbCol = mCheckProxy->rowCount(parent); + for (int i = 0; i < nbCol; ++i) { + const QModelIndex child = mCheckProxy->index(i, 0, parent); + mCheckProxy->setData(child, status ? Qt::Checked : Qt::Unchecked, Qt::CheckStateRole); + forceStatus(child, status); + } +} + +void NewMailNotifierSelectCollectionWidget::updateCollectionsRecursive(const QModelIndex &parent) +{ + const int nbCol = mCheckProxy->rowCount(parent); + for (int i = 0; i < nbCol; ++i) { + const QModelIndex child = mCheckProxy->index(i, 0, parent); + + Akonadi::Collection collection = + mCheckProxy->data(child, Akonadi::EntityTreeModel::CollectionRole).value(); + + Akonadi::NewMailNotifierAttribute *attr = collection.attribute(); + Akonadi::CollectionModifyJob *modifyJob = Q_NULLPTR; + const bool selected = (mCheckProxy->data(child, Qt::CheckStateRole).value() != 0); + if (selected && attr && attr->ignoreNewMail()) { + collection.removeAttribute(); + modifyJob = new Akonadi::CollectionModifyJob(collection); + modifyJob->setProperty("AttributeAdded", true); + } else if (!selected && (!attr || !attr->ignoreNewMail())) { + attr = collection.attribute(Akonadi::Collection::AddIfMissing); + attr->setIgnoreNewMail(true); + modifyJob = new Akonadi::CollectionModifyJob(collection); + modifyJob->setProperty("AttributeAdded", false); + } + + if (modifyJob) { + connect(modifyJob, &Akonadi::CollectionModifyJob::finished, this, &NewMailNotifierSelectCollectionWidget::slotModifyJobDone); + } + updateCollectionsRecursive(child); + } +} + +void NewMailNotifierSelectCollectionWidget::slotModifyJobDone(KJob *job) +{ + Akonadi::CollectionModifyJob *modifyJob = qobject_cast(job); + if (modifyJob && job->error()) { + if (job->property("AttributeAdded").toBool()) { + qCWarning(NEWMAILNOTIFIER_LOG) << "Failed to append NewMailNotifierAttribute to collection" + << modifyJob->collection().id() << ":" + << job->errorString(); + } else { + qCWarning(NEWMAILNOTIFIER_LOG) << "Failed to remove NewMailNotifierAttribute from collection" + << modifyJob->collection().id() << ":" + << job->errorString(); + } + } +} + diff --git a/agents/newmailnotifier/newmailnotifierselectcollectionwidget.h b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.h new file mode 100644 index 00000000..a45531b8 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifierselectcollectionwidget.h @@ -0,0 +1,68 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef NEWMAILNOTIFIERSELECTCOLLECTIONWIDGET_H +#define NEWMAILNOTIFIERSELECTCOLLECTIONWIDGET_H + +#include +#include +#include + +class QItemSelectionModel; +class KRecursiveFilterProxyModel; +namespace Akonadi +{ +class EntityTreeModel; +class ChangeRecorder; +} +class QTreeView; +class KCheckableProxyModel; +class KJob; + +class NewMailNotifierSelectCollectionWidget : public QWidget +{ + Q_OBJECT +public: + explicit NewMailNotifierSelectCollectionWidget(QWidget *parent = Q_NULLPTR); + ~NewMailNotifierSelectCollectionWidget(); + + void updateCollectionsRecursive(const QModelIndex &parent); + +private Q_SLOTS: + void slotSelectAllCollections(); + void slotUnselectAllCollections(); + void slotModifyJobDone(KJob *job); + void slotUpdateCollectionStatus(); + void slotSetCollectionFilter(const QString &); + + void slotCollectionTreeFetched(); + +private: + void updateStatus(const QModelIndex &parent); + void forceStatus(const QModelIndex &parent, bool status); + QTreeView *mFolderView; + QItemSelectionModel *mSelectionModel; + Akonadi::EntityTreeModel *mModel; + Akonadi::ChangeRecorder *mChangeRecorder; + KCheckableProxyModel *mCheckProxy; + KRecursiveFilterProxyModel *mCollectionFilter; + bool mNeedUpdate; +}; + +#endif // NEWMAILNOTIFIERSELECTCOLLECTIONWIDGET_H diff --git a/agents/newmailnotifier/newmailnotifiersettingsdialog.cpp b/agents/newmailnotifier/newmailnotifiersettingsdialog.cpp new file mode 100644 index 00000000..be057cf0 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiersettingsdialog.cpp @@ -0,0 +1,231 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "newmailnotifiersettingsdialog.h" +#include "newmailnotifierattribute.h" +#include "newmailnotifierselectcollectionwidget.h" +#include "newmailnotifieragentsettings.h" + +#include "kdepim-runtime-version.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static const char *textToSpeakMessage = + I18N_NOOP("" + "

Here you can define message. " + "You can use:

" + "
    " + "
  • %s set subject
  • " + "
  • %f set from
  • " + "
" + "
"); + +NewMailNotifierSettingsDialog::NewMailNotifierSettingsDialog(QWidget *parent) + : QDialog(parent) +{ + setWindowTitle(i18n("New Mail Notifier settings")); + setWindowIcon(QIcon::fromTheme(QStringLiteral("kmail"))); + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help); + QVBoxLayout *mainLayout = new QVBoxLayout; + setLayout(mainLayout); + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + okButton->setDefault(true); + okButton->setShortcut(Qt::CTRL | Qt::Key_Return); + connect(buttonBox, &QDialogButtonBox::accepted, this, &NewMailNotifierSettingsDialog::slotOkClicked); + connect(buttonBox, &QDialogButtonBox::rejected, this, &NewMailNotifierSettingsDialog::reject); + + QWidget *w = new QWidget; + mainLayout->addWidget(w); + mainLayout->addWidget(buttonBox); + QVBoxLayout *lay = new QVBoxLayout; + w->setLayout(lay); + QTabWidget *tab = new QTabWidget; + lay->addWidget(tab); + + QWidget *settings = new QWidget; + QVBoxLayout *vbox = new QVBoxLayout; + settings->setLayout(vbox); + + QGroupBox *grp = new QGroupBox(i18n("Choose which fields to show:")); + vbox->addWidget(grp); + QVBoxLayout *groupboxLayout = new QVBoxLayout; + grp->setLayout(groupboxLayout); + + mShowPhoto = new QCheckBox(i18n("Show Photo")); + mShowPhoto->setChecked(NewMailNotifierAgentSettings::showPhoto()); + groupboxLayout->addWidget(mShowPhoto); + + mShowFrom = new QCheckBox(i18n("Show From")); + mShowFrom->setChecked(NewMailNotifierAgentSettings::showFrom()); + groupboxLayout->addWidget(mShowFrom); + + mShowSubject = new QCheckBox(i18n("Show Subject")); + mShowSubject->setChecked(NewMailNotifierAgentSettings::showSubject()); + groupboxLayout->addWidget(mShowSubject); + + mShowFolders = new QCheckBox(i18n("Show Folders")); + mShowFolders->setChecked(NewMailNotifierAgentSettings::showFolder()); + groupboxLayout->addWidget(mShowFolders); + + mExcludeMySelf = new QCheckBox(i18n("Do not notify when email was sent by me")); + mExcludeMySelf->setChecked(NewMailNotifierAgentSettings::excludeEmailsFromMe()); + vbox->addWidget(mExcludeMySelf); + + mAllowToShowMail = new QCheckBox(i18n("Show button to display mail")); + mAllowToShowMail->setChecked(NewMailNotifierAgentSettings::showButtonToDisplayMail()); + vbox->addWidget(mAllowToShowMail); + + vbox->addStretch(); + tab->addTab(settings, i18n("Display")); + +#ifdef HAVE_SPEECH + QWidget *textSpeakWidget = new QWidget; + vbox = new QVBoxLayout; + textSpeakWidget->setLayout(vbox); + mTextToSpeak = new QCheckBox(i18n("Enabled")); + mTextToSpeak->setChecked(NewMailNotifierAgentSettings::textToSpeakEnabled()); + vbox->addWidget(mTextToSpeak); + + QLabel *howIsItWork = new QLabel(i18n("How does this work?")); + howIsItWork->setTextInteractionFlags(Qt::LinksAccessibleByMouse); + howIsItWork->setContextMenuPolicy(Qt::NoContextMenu); + vbox->addWidget(howIsItWork); + connect(howIsItWork, &QLabel::linkActivated, this, &NewMailNotifierSettingsDialog::slotHelpLinkClicked); + + QHBoxLayout *textToSpeakLayout = new QHBoxLayout; + textToSpeakLayout->setMargin(0); + QLabel *lab = new QLabel(i18n("Message:")); + textToSpeakLayout->addWidget(lab); + mTextToSpeakSetting = new QLineEdit; + mTextToSpeakSetting->setClearButtonEnabled(true); + mTextToSpeakSetting->setText(NewMailNotifierAgentSettings::textToSpeak()); + mTextToSpeakSetting->setEnabled(mTextToSpeak->isChecked()); + mTextToSpeakSetting->setWhatsThis(i18n(textToSpeakMessage)); + textToSpeakLayout->addWidget(mTextToSpeakSetting); + vbox->addLayout(textToSpeakLayout); + vbox->addStretch(); + tab->addTab(textSpeakWidget, i18n("Text to Speak")); + connect(mTextToSpeak, &QCheckBox::toggled, mTextToSpeakSetting, &QLineEdit::setEnabled); +#else + mTextToSpeak = Q_NULLPTR; + mTextToSpeakSetting = Q_NULLPTR; +#endif + + mNotify = new KNotifyConfigWidget(this); + mNotify->setApplication(QStringLiteral("akonadi_newmailnotifier_agent")); + tab->addTab(mNotify, i18n("Notify")); + + mSelectCollection = new NewMailNotifierSelectCollectionWidget; + tab->addTab(mSelectCollection, i18n("Folders")); + + KAboutData aboutData = KAboutData( + QStringLiteral("newmailnotifieragent"), + i18n("New Mail Notifier Agent"), + QStringLiteral(KDEPIM_RUNTIME_VERSION), + i18n("Notifies about new mail."), + KAboutLicense::GPL_V2, + i18n("Copyright (C) 2013-2016 Laurent Montel")); + + aboutData.addAuthor(i18n("Laurent Montel"), + i18n("Maintainer"), QStringLiteral("montel@kde.org")); + aboutData.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), + i18nc("EMAIL OF TRANSLATORS", "Your emails")); + + KHelpMenu *helpMenu = new KHelpMenu(this, aboutData, true); + //Initialize menu + QMenu *menu = helpMenu->menu(); + helpMenu->action(KHelpMenu::menuAboutApp)->setIcon(QIcon::fromTheme(QStringLiteral("kmail"))); + buttonBox->button(QDialogButtonBox::Help)->setMenu(menu); + readConfig(); +} + +NewMailNotifierSettingsDialog::~NewMailNotifierSettingsDialog() +{ + writeConfig(); +} + +static const char *myConfigGroupName = "NewMailNotifierDialog"; + +void NewMailNotifierSettingsDialog::readConfig() +{ + KConfigGroup group(KSharedConfig::openConfig(), myConfigGroupName); + + const QSize size = group.readEntry("Size", QSize(500, 300)); + if (size.isValid()) { + resize(size); + } +} + +void NewMailNotifierSettingsDialog::writeConfig() +{ + KConfigGroup group(KSharedConfig::openConfig(), myConfigGroupName); + group.writeEntry("Size", size()); + group.sync(); +} + +void NewMailNotifierSettingsDialog::slotHelpLinkClicked(const QString &) +{ + const QString help = + i18n(textToSpeakMessage); + + QWhatsThis::showText(QCursor::pos(), help); +} + +void NewMailNotifierSettingsDialog::slotOkClicked() +{ + mSelectCollection->updateCollectionsRecursive(QModelIndex()); + + NewMailNotifierAgentSettings::setShowPhoto(mShowPhoto->isChecked()); + NewMailNotifierAgentSettings::setShowFrom(mShowFrom->isChecked()); + NewMailNotifierAgentSettings::setShowSubject(mShowSubject->isChecked()); + NewMailNotifierAgentSettings::setShowFolder(mShowFolders->isChecked()); + NewMailNotifierAgentSettings::setExcludeEmailsFromMe(mExcludeMySelf->isChecked()); +#ifdef HAVE_SPEECH + NewMailNotifierAgentSettings::setTextToSpeakEnabled(mTextToSpeak->isChecked()); + NewMailNotifierAgentSettings::setTextToSpeak(mTextToSpeakSetting->text()); +#endif + NewMailNotifierAgentSettings::setShowButtonToDisplayMail(mAllowToShowMail->isChecked()); + NewMailNotifierAgentSettings::self()->save(); + mNotify->save(); + accept(); +} + diff --git a/agents/newmailnotifier/newmailnotifiersettingsdialog.h b/agents/newmailnotifier/newmailnotifiersettingsdialog.h new file mode 100644 index 00000000..dba5cdbc --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiersettingsdialog.h @@ -0,0 +1,56 @@ +/* + Copyright (c) 2013-2015 Laurent Montel + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#ifndef NEWMAILNOTIFIERSETTINGSDIALOG_H +#define NEWMAILNOTIFIERSETTINGSDIALOG_H + +#include +#include + +class KNotifyConfigWidget; +class QCheckBox; +class QLineEdit; +class NewMailNotifierSelectCollectionWidget; +class NewMailNotifierSettingsDialog : public QDialog +{ + Q_OBJECT +public: + explicit NewMailNotifierSettingsDialog(QWidget *parent = Q_NULLPTR); + ~NewMailNotifierSettingsDialog(); + +private Q_SLOTS: + void slotOkClicked(); + void slotHelpLinkClicked(const QString &); + +private: + void writeConfig(); + void readConfig(); + QCheckBox *mShowPhoto; + QCheckBox *mShowFrom; + QCheckBox *mShowSubject; + QCheckBox *mShowFolders; + QCheckBox *mExcludeMySelf; + QCheckBox *mAllowToShowMail; + KNotifyConfigWidget *mNotify; + QCheckBox *mTextToSpeak; + QLineEdit *mTextToSpeakSetting; + NewMailNotifierSelectCollectionWidget *mSelectCollection; +}; + +#endif // NEWMAILNOTIFIERSETTINGSDIALOG_H diff --git a/agents/newmailnotifier/newmailnotifiershowmessagejob.cpp b/agents/newmailnotifier/newmailnotifiershowmessagejob.cpp new file mode 100644 index 00000000..44c25e44 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiershowmessagejob.cpp @@ -0,0 +1,59 @@ +/* + Copyright (c) 2014 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, 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 "newmailnotifiershowmessagejob.h" +#include "newmailnotifier_debug.h" +#include +#include +#include +#include +#include + +NewMailNotifierShowMessageJob::NewMailNotifierShowMessageJob(Akonadi::Item::Id id, QObject *parent) + : KJob(parent), + mId(id) +{ +} + +NewMailNotifierShowMessageJob::~NewMailNotifierShowMessageJob() +{ +} + +void NewMailNotifierShowMessageJob::start() +{ + if (mId < 0) { + Q_EMIT emitResult(); + return; + } + const QString kmailInterface = QStringLiteral("org.kde.kmail"); + QDBusReply reply = QDBusConnection::sessionBus().interface()->isServiceRegistered(kmailInterface); + if (!reply.isValid() || !reply.value()) { + // Program is not already running, so start it + QString errmsg; + if (KToolInvocation::startServiceByDesktopName(QStringLiteral("kmail2"), QString(), &errmsg)) { + qCDebug(NEWMAILNOTIFIER_LOG) << " Can not start kmail" << errmsg; + setError(UserDefinedError); + Q_EMIT emitResult(); + return; + } + } + QDBusInterface kmail(kmailInterface, QStringLiteral("/KMail"), QStringLiteral("org.kde.kmail.kmail")); + if (kmail.isValid()) { + kmail.call(QStringLiteral("showMail"), mId); + } + Q_EMIT emitResult(); +} diff --git a/agents/newmailnotifier/newmailnotifiershowmessagejob.h b/agents/newmailnotifier/newmailnotifiershowmessagejob.h new file mode 100644 index 00000000..2ae0b827 --- /dev/null +++ b/agents/newmailnotifier/newmailnotifiershowmessagejob.h @@ -0,0 +1,38 @@ +/* + Copyright (c) 2014 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, 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 NEWMAILNOTIFIERSHOWMESSAGEJOB_H +#define NEWMAILNOTIFIERSHOWMESSAGEJOB_H + +#include +#include + +class NewMailNotifierShowMessageJob : public KJob +{ + Q_OBJECT +public: + explicit NewMailNotifierShowMessageJob(Akonadi::Item::Id id, QObject *parent = Q_NULLPTR); + ~NewMailNotifierShowMessageJob(); + + void start() Q_DECL_OVERRIDE; + +private: + Akonadi::Item::Id mId; + +}; + +#endif // NEWMAILNOTIFIERSHOWMESSAGEJOB_H diff --git a/agents/newmailnotifier/org.freedesktop.Akonadi.NewMailNotifier.xml b/agents/newmailnotifier/org.freedesktop.Akonadi.NewMailNotifier.xml new file mode 100644 index 00000000..c7361324 --- /dev/null +++ b/agents/newmailnotifier/org.freedesktop.Akonadi.NewMailNotifier.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/agents/newmailnotifier/specialnotifierjob.cpp b/agents/newmailnotifier/specialnotifierjob.cpp new file mode 100644 index 00000000..349f250a --- /dev/null +++ b/agents/newmailnotifier/specialnotifierjob.cpp @@ -0,0 +1,181 @@ +/* + Copyright (c) 2013-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, 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 "newmailnotifiershowmessagejob.h" +#include "specialnotifierjob.h" +#include "newmailnotifieragentsettings.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include "newmailnotifier_debug.h" + +#include +#ifdef HAVE_SPEECH +#include +#endif + +SpecialNotifierJob::SpecialNotifierJob(const QStringList &listEmails, const QString &path, Akonadi::Item::Id id, QObject *parent) + : QObject(parent), + mListEmails(listEmails), + mPath(path), + mItemId(id) +{ + Akonadi::Item item(mItemId); + Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob(item, this); + job->fetchScope().fetchPayloadPart(Akonadi::MessagePart::Envelope, true); + + connect(job, &Akonadi::ItemFetchJob::result, this, &SpecialNotifierJob::slotItemFetchJobDone); +} + +SpecialNotifierJob::~SpecialNotifierJob() +{ + +} + +void SpecialNotifierJob::setDefaultPixmap(const QPixmap &pixmap) +{ + mDefaultPixmap = pixmap; +} + +void SpecialNotifierJob::slotItemFetchJobDone(KJob *job) +{ + if (job->error()) { + qCWarning(NEWMAILNOTIFIER_LOG) << job->errorString(); + deleteLater(); + return; + } + + const Akonadi::Item::List lst = qobject_cast(job)->items(); + if (lst.count() == 1) { + const Akonadi::Item item = lst.first(); + if (!item.hasPayload()) { + qCDebug(NEWMAILNOTIFIER_LOG) << " message has not payload."; + deleteLater(); + return; + } + + const KMime::Message::Ptr mb = item.payload(); + mFrom = mb->from()->asUnicodeString(); + mSubject = mb->subject()->asUnicodeString(); + if (NewMailNotifierAgentSettings::showPhoto()) { + Akonadi::ContactSearchJob *job = new Akonadi::ContactSearchJob(this); + job->setLimit(1); + job->setQuery(Akonadi::ContactSearchJob::Email, KEmailAddress::firstEmailAddress(mFrom).toLower(), Akonadi::ContactSearchJob::ExactMatch); + connect(job, &Akonadi::ItemFetchJob::result, this, &SpecialNotifierJob::slotSearchJobFinished); + } else { + emitNotification(mDefaultPixmap); + deleteLater(); + } + } else { + qCWarning(NEWMAILNOTIFIER_LOG) << " Found item different from 1: " << lst.count(); + deleteLater(); + return; + } +} + +void SpecialNotifierJob::slotSearchJobFinished(KJob *job) +{ + const Akonadi::ContactSearchJob *searchJob = qobject_cast(job); + if (searchJob->error()) { + qCWarning(NEWMAILNOTIFIER_LOG) << "Unable to fetch contact:" << searchJob->errorText(); + emitNotification(mDefaultPixmap); + return; + } + if (!searchJob->contacts().isEmpty()) { + const KContacts::Addressee addressee = searchJob->contacts().at(0); + const KContacts::Picture photo = addressee.photo(); + const QImage image = photo.data(); + if (image.isNull()) { + emitNotification(mDefaultPixmap); + } else { + emitNotification(QPixmap::fromImage(image)); + } + } else { + emitNotification(mDefaultPixmap); + } +} + +void SpecialNotifierJob::emitNotification(const QPixmap &pixmap) +{ + if (NewMailNotifierAgentSettings::excludeEmailsFromMe()) { + Q_FOREACH (const QString &email, mListEmails) { + if (mFrom.contains(email)) { + //Exclude this notification + deleteLater(); + return; + } + } + } + + QStringList result; + if (NewMailNotifierAgentSettings::showFrom()) { + result << i18n("From: %1", mFrom.toHtmlEscaped()); + } + if (NewMailNotifierAgentSettings::showSubject()) { + QString subject = mSubject.simplified(); + if (subject.length() > 80) { + subject.truncate(80); + subject += QStringLiteral("..."); + } + result << i18n("Subject: %1", subject.toHtmlEscaped()); + } + if (NewMailNotifierAgentSettings::showFolder()) { + result << i18n("In: %1", mPath); + } + + if (NewMailNotifierAgentSettings::textToSpeakEnabled()) { + if (!NewMailNotifierAgentSettings::textToSpeak().isEmpty()) { +#ifdef HAVE_SPEECH + QTextToSpeech *speech = new QTextToSpeech(this); + QString message = NewMailNotifierAgentSettings::textToSpeak(); + message.replace(QStringLiteral("%s"), mSubject.toHtmlEscaped()); + message.replace(QStringLiteral("%f"), mFrom.toHtmlEscaped()); + speech->say(message); +#endif + } + } + + if (NewMailNotifierAgentSettings::showButtonToDisplayMail()) { + KNotification *notification = new KNotification(QStringLiteral("new-email"), Q_NULLPTR, KNotification::CloseOnTimeout); + notification->setText(result.join(QStringLiteral("\n"))); + notification->setPixmap(pixmap); + notification->setActions(QStringList() << i18n("Show mail...")); + + connect(notification, static_cast(&KNotification::activated), this, &SpecialNotifierJob::slotOpenMail); + connect(notification, &KNotification::closed, this, &SpecialNotifierJob::deleteLater); + + notification->sendEvent(); + } else { + Q_EMIT displayNotification(pixmap, result.join(QStringLiteral("\n"))); + deleteLater(); + } +} + +void SpecialNotifierJob::slotOpenMail() +{ + NewMailNotifierShowMessageJob *job = new NewMailNotifierShowMessageJob(mItemId); + job->start(); +} diff --git a/agents/newmailnotifier/specialnotifierjob.h b/agents/newmailnotifier/specialnotifierjob.h new file mode 100644 index 00000000..ddd465d3 --- /dev/null +++ b/agents/newmailnotifier/specialnotifierjob.h @@ -0,0 +1,53 @@ +/* + Copyright (c) 2013-2016 Montel Laurent + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License, version 2, 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 SPECIALNOTIFIERJOB_H +#define SPECIALNOTIFIERJOB_H + +#include +#include +#include +#include +class KJob; + +class SpecialNotifierJob : public QObject +{ + Q_OBJECT +public: + explicit SpecialNotifierJob(const QStringList &listEmails, const QString &path, Akonadi::Item::Id id, QObject *parent = Q_NULLPTR); + ~SpecialNotifierJob(); + + void setDefaultPixmap(const QPixmap &pixmap); + +Q_SIGNALS: + void displayNotification(const QPixmap &pixmap, const QString &message); + +private Q_SLOTS: + void slotSearchJobFinished(KJob *job); + void slotItemFetchJobDone(KJob *); + void slotOpenMail(); +private: + void emitNotification(const QPixmap &pixmap); + QPixmap mDefaultPixmap; + QStringList mListEmails; + QString mSubject; + QString mFrom; + QString mPath; + Akonadi::Item::Id mItemId; +}; + +#endif // SPECIALNOTIFIERJOB_H diff --git a/akonadi-prefix.h.cmake b/akonadi-prefix.h.cmake new file mode 100644 index 00000000..b2753f10 --- /dev/null +++ b/akonadi-prefix.h.cmake @@ -0,0 +1,6 @@ +/* This file contains all the paths that change when changing the installation prefix */ + +#define AKONADIPREFIX "${CMAKE_INSTALL_PREFIX}" +#define AKONADIDATA "${SHARE_INSTALL_PREFIX}" +#define AKONADICONFIG "${CONFIG_INSTALL_DIR}" + diff --git a/akonadi-version.h.cmake b/akonadi-version.h.cmake new file mode 100644 index 00000000..58c028e4 --- /dev/null +++ b/akonadi-version.h.cmake @@ -0,0 +1,25 @@ +/* + This file is part of kdepim. + Copyright (C) 2009 Christophe Giboudeaux + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef AKONADI_VERSION_H +#define AKONADI_VERSION_H + +#define AKONADI_VERSION "@AKONADI_VERSION@" + +#endif // AKONADI_VERSION_H \ No newline at end of file diff --git a/cmake/modules/FindXsltproc.cmake b/cmake/modules/FindXsltproc.cmake new file mode 100644 index 00000000..45b46cfc --- /dev/null +++ b/cmake/modules/FindXsltproc.cmake @@ -0,0 +1,32 @@ +# Find xsltproc executable and provide a macro to generate D-Bus interfaces. +# +# The following variables are defined : +# XSLTPROC_EXECUTABLE - path to the xsltproc executable +# Xsltproc_FOUND - true if the program was found +# +find_program(XSLTPROC_EXECUTABLE xsltproc DOC "Path to the xsltproc executable") +mark_as_advanced(XSLTPROC_EXECUTABLE) + +if(XSLTPROC_EXECUTABLE) + set(Xsltproc_FOUND TRUE) + + # We depend on kdepimlibs, make sure it's found + if(NOT DEFINED KF5Akonadi_DATA_DIR) + find_package(KF5Akonadi REQUIRED) + endif() + + + # Macro to generate a D-Bus interface description from a KConfigXT file + macro(kcfg_generate_dbus_interface _kcfg _name) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + COMMAND ${XSLTPROC_EXECUTABLE} --stringparam interfaceName ${_name} + ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + > ${CMAKE_CURRENT_BINARY_DIR}/${_name}.xml + DEPENDS ${KF5Akonadi_DATA_DIR}/kcfg2dbus.xsl + ${_kcfg} + ) + endmacro() +endif() + diff --git a/defaultsetup/CMakeLists.txt b/defaultsetup/CMakeLists.txt new file mode 100644 index 00000000..c0394b8e --- /dev/null +++ b/defaultsetup/CMakeLists.txt @@ -0,0 +1,10 @@ +configure_file(defaultaddressbook.desktop ${CMAKE_CURRENT_BINARY_DIR}/defaultaddressbook) +configure_file(defaultcalendar.desktop ${CMAKE_CURRENT_BINARY_DIR}/defaultcalendar) +configure_file(defaultnotebook.desktop ${CMAKE_CURRENT_BINARY_DIR}/defaultnotebook) +configure_file(birthdaycalendar.desktop ${CMAKE_CURRENT_BINARY_DIR}/birthdaycalendar) + +install ( FILES ${CMAKE_CURRENT_BINARY_DIR}/defaultcalendar + ${CMAKE_CURRENT_BINARY_DIR}/defaultaddressbook + ${CMAKE_CURRENT_BINARY_DIR}/defaultnotebook + ${CMAKE_CURRENT_BINARY_DIR}/birthdaycalendar + DESTINATION ${KDE_INSTALL_DATADIR}/akonadi/firstrun ) diff --git a/defaultsetup/birthdaycalendar.desktop b/defaultsetup/birthdaycalendar.desktop new file mode 100644 index 00000000..53aa1e80 --- /dev/null +++ b/defaultsetup/birthdaycalendar.desktop @@ -0,0 +1,3 @@ +[Agent] +Id=birthdaycalendar +Type=akonadi_birthdays_resource diff --git a/defaultsetup/defaultaddressbook.desktop b/defaultsetup/defaultaddressbook.desktop new file mode 100644 index 00000000..cb010138 --- /dev/null +++ b/defaultsetup/defaultaddressbook.desktop @@ -0,0 +1,56 @@ +[Agent] +Id=defaultaddressbook +Type=akonadi_contacts_resource +Name=Personal Contacts +Name[ast]=Contautos personales +Name[bg]=Лични контакти +Name[bs]=Lični kontakti +Name[ca]=Contactes personals +Name[ca@valencia]=Contactes personals +Name[cs]=Osobní kontakty +Name[da]=Personlige kontakter +Name[de]=Persönliche Kontakte +Name[el]=Προσωπικές επαφές +Name[en_GB]=Personal Contacts +Name[es]=Contactos personales +Name[et]=Isiklikud kontaktid +Name[fi]=Omat yhteystiedot +Name[fr]=Contacts personnels +Name[ga]=Teagmhálacha Pearsanta +Name[gl]=Contactos Persoais +Name[hu]=Személyes névjegyek +Name[ia]=Contactos personal +Name[it]=Contatti personali +Name[ja]=個人の連絡先 +Name[kk]=Дербес контакттар +Name[km]=ទំនាក់ទំនង​ផ្ទាល់ខ្លួន +Name[ko]=개인 연락처 +Name[lt]=Asmeniniai kontaktai +Name[lv]=Personīgie kontakti +Name[nb]=Personlige kontakter +Name[nds]=Persöönlich Kontakten +Name[nl]=Persoonlijke contacten +Name[nn]=Personlege kontaktar +Name[pa]=ਨਿੱਜੀ ਸੰਪਰਕ +Name[pl]=Kontakty osobiste +Name[pt]=Contactos Pessoais +Name[pt_BR]=Contatos pessoais +Name[ro]=Contacte personale +Name[ru]=Личные контакты +Name[sk]=Osobné kontakty +Name[sl]=Osebni stiki +Name[sr]=Лични контакти +Name[sr@ijekavian]=Лични контакти +Name[sr@ijekavianlatin]=Lični kontakti +Name[sr@latin]=Lični kontakti +Name[sv]=Personliga kontakter +Name[tr]=Kişisel Bağlantılar +Name[ug]=شەخسىي ئالاقەداشلار +Name[uk]=Особисті контакти +Name[x-test]=xxPersonal Contactsxx +Name[zh_CN]=个人联系人 +Name[zh_TW]=個人聯絡人 + +[Settings] +IsConfigured=true +Path[$e]=$HOME/.local/share/contacts/ diff --git a/defaultsetup/defaultcalendar.desktop b/defaultsetup/defaultcalendar.desktop new file mode 100644 index 00000000..2b8c8c99 --- /dev/null +++ b/defaultsetup/defaultcalendar.desktop @@ -0,0 +1,52 @@ +[Agent] +Id=defaultcalendar +Type=akonadi_ical_resource +Name=Personal Calendar +Name[ast]=Calendariu personal +Name[bg]=Личен календар +Name[bs]=Lični kalendar +Name[ca]=Calendari personal +Name[ca@valencia]=Calendari personal +Name[cs]=Osobní kalendář +Name[da]=Personlig kalender +Name[de]=Persönlicher Kalender +Name[el]=Προσωπικό ημερολόγιο +Name[en_GB]=Personal Calendar +Name[es]=Calendario personal +Name[et]=Isiklik kalender +Name[fi]=Oma kalenteri +Name[fr]=Agenda personnel +Name[ga]=Féilire Pearsanta +Name[gl]=Calendario persoal +Name[hu]=Személyes naptár +Name[ia]=Calendario Personal +Name[it]=Calendario personale +Name[kk]=Дербес күнтізбе +Name[km]=ប្រតិទិន​ផ្ទាល់ខ្លួន +Name[ko]=개인 달력 +Name[lt]=Asmeninis kalendorius +Name[lv]=Personīgais kalendārs +Name[nb]=Personlig kalender +Name[nds]=Persöönlich Kalenner +Name[nl]=Persoonlijke agenda +Name[pl]=Kalendarz osobisty +Name[pt]=Calendário Pessoal +Name[pt_BR]=Calendário pessoal +Name[ro]=Calendar personal +Name[ru]=Личный календарь +Name[sk]=Osobný kalendár +Name[sl]=Osebni koledar +Name[sr]=Лични календар +Name[sr@ijekavian]=Лични календар +Name[sr@ijekavianlatin]=Lični kalendar +Name[sr@latin]=Lični kalendar +Name[sv]=Personlig kalender +Name[tr]=Kişisel Takvim +Name[uk]=Особистий календар +Name[x-test]=xxPersonal Calendarxx +Name[zh_CN]=个人日历 +Name[zh_TW]=個人行事曆 + +[Settings] +Path[$e]=$HOME/.local/share/apps/korganizer/std.ics + diff --git a/defaultsetup/defaultnotebook.desktop b/defaultsetup/defaultnotebook.desktop new file mode 100644 index 00000000..753431af --- /dev/null +++ b/defaultsetup/defaultnotebook.desktop @@ -0,0 +1,79 @@ +[Agent] +Id=defaultnotebook +Type=akonadi_akonotes_resource +Name=Notes +Name[af]=Notas +Name[ar]=ملاحظات +Name[ast]=Notes +Name[be]=Заметкі +Name[bg]=Бележки +Name[br]=Notennoù +Name[bs]=Bilješke +Name[ca]=Notes +Name[ca@valencia]=Notes +Name[cs]=Poznámky +Name[cy]=Nodiadau +Name[da]=Noter +Name[de]=Notizen +Name[el]=Σημειώσεις +Name[en_GB]=Notes +Name[eo]=Notoj +Name[es]=Notas +Name[et]=Sedelid +Name[eu]=Oharrak +Name[fa]=یادداشتها +Name[fi]=Muistiinpanot +Name[fr]=Notes +Name[fy]=Notysjes +Name[ga]=Nótaí +Name[gl]=Notas +Name[he]=פתקים +Name[hu]=Feljegyzések +Name[ia]=Notas +Name[is]=Minnismiðar +Name[it]=Note +Name[ja]=メモ +Name[ka]=ჩანიშვნები +Name[kk]=Жазбалар +Name[km]=ចំណាំ +Name[ko]=노트 +Name[lt]=Užrašai +Name[lv]=Piezīmes +Name[mai]=टिप्पणी +Name[mk]=Белешки +Name[ms]=Nota +Name[nb]=Notater +Name[nds]=Notizen +Name[ne]=टिपोट +Name[nl]=Notities +Name[nn]=Notat +Name[pa]=ਨੋਟਿਸ +Name[pl]=Notatki +Name[pt]=Notas +Name[pt_BR]=Notas +Name[ro]=Notițe +Name[ru]=Заметки +Name[se]=Nohtat +Name[sk]=Poznámky +Name[sl]=Sporočilca +Name[sq]=Shënimet +Name[sr]=Белешке +Name[sr@ijekavian]=Биљешке +Name[sr@ijekavianlatin]=Bilješke +Name[sr@latin]=Beleške +Name[sv]=Anteckningar +Name[ta]=குறிப்புகள் +Name[tg]=Ахборот +Name[th]=บันทึกย่อ +Name[tr]=Notlar +Name[ug]=ئىزاھ +Name[uk]=Примітки +Name[uz]=Yozma xotira +Name[uz@cyrillic]=Ёзма хотира +Name[wa]=Notes +Name[x-test]=xxNotesxx +Name[zh_CN]=便笺 +Name[zh_TW]=備忘錄 + +[Settings] +Path[$e]=$HOME/.local/share/notes/ diff --git a/doc/git-migration.txt b/doc/git-migration.txt new file mode 100644 index 00000000..2908dd65 --- /dev/null +++ b/doc/git-migration.txt @@ -0,0 +1,7 @@ +Migrated to git using the following: + +svn2git: http://gitorious.org/svn2git version: 409d8bc4cbaade82672f251c45178c3cfed4619d +kde-ruleset: http://projects.kde.org/projects/playground/sdk/kde-ruleset/ version: d71b11d9434d951bdaaff0f7cd3de9dcae000018 +kde svn repo synced at revision: 1208541 + +command: time ionice -c3 nice svn-all-fast-export --identity-map ../kde-ruleset/account-map --rules ../kde-ruleset/kdepim-runtime-rules --add-metadata --debug-rules --stats --svn-branches ../svn/ diff --git a/doc/libakonadi.xmi b/doc/libakonadi.xmi new file mode 100644 index 00000000..72f35f35 --- /dev/null +++ b/doc/libakonadi.xmi @@ -0,0 +1,9639 @@ + + + + + umbrello uml modeller http://uml.sf.net + 1.5.3 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ +
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+ +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+ + + +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+ +
+
+ +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ + +
+ +
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ + +
+ +
+ +
+ +
+ +
+ +
+
+
+ +
+ +
+ +
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+
+
+
diff --git a/doc/pics/akonadi_agent_handling.eps b/doc/pics/akonadi_agent_handling.eps new file mode 100644 index 00000000..974edc15 --- /dev/null +++ b/doc/pics/akonadi_agent_handling.eps @@ -0,0 +1,1142 @@ +%!PS-Adobe-1.0 EPSF-3.0 +%%BoundingBox: 35 656 610 819 +%%Creator: Qt 3.3.6 +%%CreationDate: Do Sep 28 16:06:56 2006 +%%Orientation: Portrait +%%Pages: 1 +%%DocumentFonts: BitstreamVeraSerif-Roman + +%%EndComments +%%BeginProlog +% Prolog copyright 1994-2005 Trolltech. You may copy this prolog in any way +% that is directly related to this document. For other use of this prolog, +% see your licensing agreement for Qt. +/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D +/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D +/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D +/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read +pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end +d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi +false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88 +0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{ +LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{ +gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get +SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7 +bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL +0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB +exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL +ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1 +eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if +64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3 +i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll +putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3 +1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D +/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3 +colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1 +QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray +QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2 +add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq +{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC +imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel +where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d +/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7 +DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d +/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height +h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4 +DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{ +pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC +WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{ +1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie} +if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4 +2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg +RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0 +exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if +BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h +add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0 +6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT +}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h +D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div +add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1 +ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT +0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy +MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R +{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h +ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry +D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y +w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul +200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90 +x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0 +-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h +ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale +NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS} +D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT +x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP +ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255 +div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0 +B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25 +/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true +exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3 +-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch +/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup +maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding +fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length +dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end +definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d} +D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty +MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch +stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT +1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0 +exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore +showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop +pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D +/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt +ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP} +D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D + +/LArr[ [] [] [ 10.417 3.125 ] [ 3.125 10.417 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d +/pageinit { +35.52 24 translate +% 185*280mm (portrait) +0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d +%%EndProlog +%%BeginSetup +% Fonts and encodings used +/BitstreamVeraSerif-RomanList [ +[ /BitstreamVeraSerif-Roman 1.0 0.0 ] + [ /BitstreamVeraSerif 1.0 0.0 ] + [ /Helvetica 0.988 0.000 ] +] d +%%BeginFont: Bitstream Vera Serif +%!PS-Adobe-3.0 Resource-Font +%%Copyright: Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. +%%Creator: Converted from TrueType by Qt +25 dict begin +/_d{bind def}bind def +/_m{moveto}_d +/_l{lineto}_d +/_cl{closepath eofill}_d +/_c{curveto}_d +/_sc{7 -1 roll{setcachedevice}{pop pop pop pop pop pop}ifelse}_d +/_e{exec}_d +/FontName /BitstreamVeraSerif-Roman def +/PaintType 0 def +/FontMatrix[.001 0 0 .001 0 0]def +/FontBBox[-182 -235 1287 928]def +/FontType 3 def +/Encoding StandardEncoding def +/FontInfo 10 dict dup begin +/FamilyName (Bitstream Vera Serif) def +/FullName (Bitstream Vera Serif) def +/Notice (Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.) def +/Weight (Roman) def +/Version (Release 1.10) def +/ItalicAngle 0.0 def +/isFixedPitch false def +/UnderlinePosition -213 def +/UnderlineThickness 133 def +end readonly def +/CharStrings 32 dict dup begin +/.notdef{600 0 50 -176 550 705 _sc +50 -176 _m +50 705 _l +550 705 _l +550 -176 _l +50 -176 _l +106 -120 _m +494 -120 _l +494 649 _l +106 649 _l +106 -120 _l +_cl}_d +/space{318 0 0 0 0 0 _sc +}_d +/parenleft{390 0 79 -155 319 760 _sc +319 -155 _m +239 -119 179 -63 139 13 _c +99 89 79 186 79 302 _c +79 418 99 514 139 591 _c +179 668 239 724 319 760 _c +319 712 _l +269 677 233 628 211 566 _c +189 503 178 415 178 302 _c +178 188 189 100 211 38 _c +233 -24 269 -72 319 -107 _c +319 -155 _l +_cl}_d +/parenright{390 0 71 -155 311 760 _sc +71 -155 _m +71 -107 _l +121 -72 157 -24 179 38 _c +201 100 212 188 212 302 _c +212 415 201 503 179 566 _c +157 628 121 677 71 712 _c +71 760 _l +150 724 210 668 250 591 _c +290 514 311 418 311 302 _c +311 186 290 89 250 13 _c +210 -63 150 -119 71 -155 _c +_cl}_d +/colon{337 0 104 -13 234 434 _sc +104 51 _m +104 69 110 84 123 97 _c +135 109 151 116 169 116 _c +187 116 202 109 215 97 _c +227 84 234 69 234 51 _c +234 32 227 17 215 5 _c +203 -7 187 -13 169 -13 _c +150 -13 134 -7 122 5 _c +110 17 104 32 104 51 _c +104 369 _m +104 387 110 402 123 415 _c +135 427 151 434 169 434 _c +187 434 203 427 215 415 _c +227 403 234 387 234 369 _c +234 350 227 334 215 322 _c +203 310 187 304 169 304 _c +151 304 135 310 123 323 _c +110 335 104 351 104 369 _c +_cl}_d +/A{722 0 -5 0 732 729 _sc +200 264 _m +468 264 _l +334 611 _l +200 264 _l +-5 0 _m +-5 52 _l +58 52 _l +318 729 _l +400 729 _l +660 52 _l +732 52 _l +732 0 _l +467 0 _l +467 52 _l +548 52 _l +487 212 _l +180 212 _l +119 52 _l +199 52 _l +199 0 _l +-5 0 _l +_cl}_d +/B{{735 0 55 0 674 729 _sc +247 52 _m +393 52 _l +451 52 494 64 521 90 _c +548 115 562 155 562 211 _c +562 265 548 305 521 331 _c +494 356 451 369 393 369 _c +247 369 _l +247 52 _l +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +415 729 _l +488 729 543 714 581 684 _c +618 654 637 609 637 549 _c +637 505 624 471 598 445 _c +572 419 535 404 485 398 _c +547 390 594 370 626 338 _c +658 306 674 264 674 211 _c +674 139 651 85 605 51 _c +559 17 488 0 392 0 _c +55 0 _l +247 421 _m +371 421 _l +424 421 463 431 488 451 _c +512 471 525 504 525 549 _c +}_e{525 593 512 626 488 646 _c +463 666 424 677 371 677 _c +247 677 _l +247 421 _l +_cl}_e}_d +/C{{765 0 56 -13 705 742 _sc +705 193 _m +683 125 647 73 597 39 _c +546 4 482 -13 405 -13 _c +357 -13 312 -5 272 11 _c +231 27 195 50 164 82 _c +127 118 100 160 82 206 _c +64 252 56 305 56 364 _c +56 477 88 568 154 638 _c +219 707 305 742 413 742 _c +453 742 495 736 540 726 _c +584 716 633 700 685 679 _c +685 511 _l +630 511 _l +618 572 593 617 557 646 _c +521 675 470 690 405 690 _c +327 690 268 662 228 607 _c +188 551 168 470 168 364 _c +168 257 188 176 228 121 _c +268 65 327 38 405 38 _c +459 38 503 51 539 77 _c +574 103 599 141 615 193 _c +705 193 _l +_cl}_e}_d +/D{802 0 55 0 744 729 _sc +247 52 _m +338 52 _l +432 52 505 79 556 133 _c +606 187 632 264 632 365 _c +632 466 606 543 556 597 _c +505 650 432 677 338 677 _c +247 677 _l +247 52 _l +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +345 729 _l +471 729 569 697 639 633 _c +709 569 744 479 744 365 _c +744 250 708 160 638 96 _c +568 32 470 0 345 0 _c +55 0 _l +_cl}_d +/I{395 0 55 0 340 729 _sc +247 52 _m +340 52 _l +340 0 _l +55 0 _l +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +340 729 _l +340 677 _l +247 677 _l +247 52 _l +_cl}_d +/M{1024 0 50 0 973 729 _sc +55 0 _m +55 52 _l +148 52 _l +148 677 _l +50 677 _l +50 729 _l +262 729 _l +518 210 _l +774 729 _l +973 729 _l +973 677 _l +876 677 _l +876 52 _l +969 52 _l +969 0 _l +684 0 _l +684 52 _l +777 52 _l +777 615 _l +527 107 _l +458 107 _l +208 615 _l +208 52 _l +301 52 _l +301 0 _l +55 0 _l +_cl}_d +/O{{820 0 56 -13 764 742 _sc +410 38 _m +490 38 550 65 591 120 _c +631 175 652 256 652 364 _c +652 471 631 552 591 607 _c +550 662 490 690 410 690 _c +330 690 269 662 229 607 _c +188 552 168 471 168 364 _c +168 256 188 175 229 120 _c +269 65 330 38 410 38 _c +410 -13 _m +360 -13 315 -5 273 11 _c +231 27 195 50 164 82 _c +127 118 100 160 82 206 _c +64 252 56 304 56 364 _c +56 422 64 475 82 521 _c +100 567 127 609 164 646 _c +196 678 232 702 273 718 _c +314 734 360 742 410 742 _c +516 742 601 707 666 638 _c +731 568 764 477 764 364 _c +764 305 755 252 737 206 _c +719 159 692 118 656 82 _c +624 50 587 26 546 10 _c +505 -5 460 -13 410 -13 _c +_cl}_e}_d +/Q{{820 0 56 -159 764 742 _sc +422 -13 _m +310 -13 221 21 155 89 _c +89 157 56 248 56 364 _c +56 422 64 475 82 521 _c +100 567 127 609 164 646 _c +196 678 232 702 273 718 _c +314 734 360 742 410 742 _c +516 742 601 707 666 638 _c +731 568 764 477 764 364 _c +764 267 739 186 691 120 _c +642 54 575 13 489 -5 _c +506 -27 527 -43 553 -53 _c +578 -63 608 -69 644 -69 _c +659 -69 _l +659 -159 _l +604 -156 557 -142 518 -118 _c +478 -94 446 -59 422 -13 _c +410 38 _m +490 38 550 65 591 120 _c +631 175 652 256 652 364 _c +652 471 631 552 591 607 _c +550 662 490 690 410 690 _c +330 690 269 662 229 607 _c +188 552 168 471 168 364 _c +168 256 188 175 229 120 _c +269 65 330 38 410 38 _c +_cl}_e}_d +/R{{753 0 55 0 777 729 _sc +479 362 _m +501 356 521 345 538 330 _c +554 315 569 294 582 268 _c +688 52 _l +777 52 _l +777 0 _l +605 0 _l +491 232 _l +469 276 449 305 431 319 _c +413 332 388 339 356 339 _c +247 339 _l +247 52 _l +350 52 _l +350 0 _l +55 0 _l +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +425 729 _l +495 729 550 712 589 678 _c +627 644 647 596 647 534 _c +647 484 633 444 605 416 _c +577 387 535 369 479 362 _c +247 391 _m +391 391 _l +440 391 476 402 500 426 _c +523 449 535 485 535 534 _c +}_e{535 582 523 618 500 642 _c +476 665 440 677 391 677 _c +247 677 _l +247 391 _l +_cl}_e}_d +/a{{596 0 50 -13 568 533 _sc +398 163 _m +398 273 _l +282 273 _l +237 273 204 263 182 244 _c +160 224 150 195 150 156 _c +150 120 161 91 183 70 _c +205 48 235 38 273 38 _c +310 38 340 49 363 72 _c +386 95 398 125 398 163 _c +488 324 _m +488 52 _l +568 52 _l +568 0 _l +398 0 _l +398 56 _l +378 32 355 14 329 3 _c +303 -7 272 -13 238 -13 _c +180 -13 134 2 100 32 _c +66 62 50 104 50 156 _c +50 209 69 250 108 280 _c +146 310 201 325 272 325 _c +398 325 _l +398 361 _l +398 400 386 430 362 452 _c +338 474 304 485 261 485 _c +225 485 197 476 176 460 _c +154 444 141 420 136 388 _c +90 388 _l +}_e{90 493 _l +121 506 151 516 181 523 _c +210 529 239 533 267 533 _c +339 533 393 515 431 479 _c +469 443 488 392 488 324 _c +_cl}_e}_d +/b{{640 0 29 -13 590 760 _sc +115 52 _m +115 708 _l +29 708 _l +29 760 _l +205 760 _l +205 438 _l +222 470 244 494 272 510 _c +299 525 333 533 373 533 _c +437 533 489 507 529 457 _c +569 407 590 341 590 260 _c +590 178 569 112 529 62 _c +489 12 437 -13 373 -13 _c +333 -13 299 -5 272 9 _c +244 24 222 48 205 81 _c +205 0 _l +29 0 _l +29 52 _l +115 52 _l +205 234 _m +205 171 217 123 241 91 _c +265 58 299 42 345 42 _c +391 42 425 60 449 97 _c +473 133 485 188 485 260 _c +485 332 473 386 449 422 _c +425 458 391 477 345 477 _c +299 477 265 460 241 427 _c +217 394 205 347 205 285 _c +205 234 _l +}_e{_cl}_e}_d +/c{{560 0 50 -13 514 533 _sc +514 156 _m +501 100 477 58 441 30 _c +405 1 358 -13 301 -13 _c +225 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 73 408 119 458 _c +165 508 225 533 301 533 _c +333 533 366 529 399 521 _c +431 513 464 502 497 487 _c +497 354 _l +445 354 _l +438 399 423 432 400 453 _c +377 474 344 485 302 485 _c +253 485 216 466 192 428 _c +167 390 155 334 155 260 _c +155 184 167 128 192 90 _c +216 52 253 34 302 34 _c +340 34 371 44 394 64 _c +417 84 433 115 442 156 _c +514 156 _l +_cl}_e}_d +/d{{640 0 50 -13 611 760 _sc +525 52 _m +611 52 _l +611 0 _l +435 0 _l +435 81 _l +417 48 395 24 368 9 _c +340 -5 307 -13 267 -13 _c +203 -13 150 12 110 62 _c +70 112 50 178 50 260 _c +50 341 70 407 110 457 _c +150 507 203 533 267 533 _c +307 533 340 525 368 510 _c +395 494 417 470 435 438 _c +435 708 _l +350 708 _l +350 760 _l +525 760 _l +525 52 _l +435 234 _m +435 285 _l +435 347 423 394 399 427 _c +375 460 340 477 295 477 _c +249 477 214 458 190 422 _c +166 386 155 332 155 260 _c +155 188 166 133 190 97 _c +214 60 249 42 295 42 _c +340 42 375 58 399 91 _c +423 123 435 171 435 234 _c +}_e{_cl}_e}_d +/e{{592 0 50 -13 542 533 _sc +542 250 _m +155 250 _l +155 246 _l +155 176 168 123 194 87 _c +220 51 259 34 311 34 _c +350 34 382 44 408 65 _c +433 85 451 116 461 157 _c +533 157 _l +519 100 492 57 454 29 _c +415 1 364 -13 302 -13 _c +226 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 72 408 118 458 _c +163 508 222 533 296 533 _c +374 533 435 508 477 460 _c +519 412 540 342 542 250 _c +436 302 _m +434 362 421 408 397 439 _c +373 469 340 485 296 485 _c +254 485 222 469 198 438 _c +174 407 160 362 155 302 _c +436 302 _l +_cl}_e}_d +/g{{640 0 50 -221 611 533 _sc +525 467 _m +525 11 _l +525 -63 504 -120 463 -160 _c +422 -200 364 -221 288 -221 _c +254 -221 221 -218 190 -212 _c +158 -206 128 -196 100 -184 _c +100 -75 _l +147 -75 _l +153 -109 166 -133 188 -149 _c +210 -165 241 -173 282 -173 _c +334 -173 373 -158 398 -128 _c +422 -98 435 -51 435 11 _c +435 81 _l +417 48 395 24 368 9 _c +340 -5 307 -13 267 -13 _c +203 -13 150 12 110 62 _c +70 112 50 178 50 260 _c +50 341 70 407 110 457 _c +150 507 203 533 267 533 _c +307 533 340 525 368 510 _c +395 494 417 470 435 438 _c +435 519 _l +611 519 _l +611 467 _l +525 467 _l +435 285 _m +}_e{435 347 423 394 399 427 _c +375 460 340 477 295 477 _c +249 477 214 458 190 422 _c +166 386 155 332 155 260 _c +155 188 166 133 190 97 _c +214 60 249 42 295 42 _c +340 42 375 58 399 91 _c +423 123 435 171 435 234 _c +435 285 _l +_cl}_e}_d +/h{{644 0 36 0 616 760 _sc +41 0 _m +41 52 _l +122 52 _l +122 708 _l +36 708 _l +36 760 _l +212 760 _l +212 427 _l +228 461 250 488 276 506 _c +302 524 333 533 369 533 _c +426 533 468 516 495 484 _c +522 451 536 400 536 330 _c +536 52 _l +616 52 _l +616 0 _l +368 0 _l +368 52 _l +446 52 _l +446 302 _l +446 365 438 408 422 432 _c +406 455 379 467 340 467 _c +298 467 266 451 244 421 _c +222 391 212 347 212 289 _c +212 52 _l +290 52 _l +290 0 _l +41 0 _l +_cl}_e}_d +/i{320 0 36 0 297 736 _sc +97 680 _m +97 695 102 708 113 719 _c +124 730 137 736 153 736 _c +167 736 180 730 191 719 _c +202 708 208 695 208 680 _c +208 664 202 651 192 641 _c +181 630 168 625 153 625 _c +137 625 124 630 113 641 _c +102 651 97 664 97 680 _c +212 52 _m +297 52 _l +297 0 _l +36 0 _l +36 52 _l +122 52 _l +122 467 _l +36 467 _l +36 519 _l +212 519 _l +212 52 _l +_cl}_d +/k{{606 0 29 0 613 760 _sc +286 0 _m +34 0 _l +34 52 _l +115 52 _l +115 708 _l +29 708 _l +29 760 _l +205 760 _l +205 265 _l +424 467 _l +349 467 _l +349 519 _l +584 519 _l +584 467 _l +495 467 _l +341 324 _l +538 52 _l +613 52 _l +613 0 _l +357 0 _l +357 52 _l +431 52 _l +276 265 _l +205 199 _l +205 52 _l +286 52 _l +286 0 _l +_cl}_e}_d +/l{320 0 29 0 290 760 _sc +205 52 _m +290 52 _l +290 0 _l +29 0 _l +29 52 _l +115 52 _l +115 708 _l +29 708 _l +29 760 _l +205 760 _l +205 52 _l +_cl}_d +/n{{644 0 36 0 616 533 _sc +41 0 _m +41 52 _l +122 52 _l +122 467 _l +36 467 _l +36 519 _l +212 519 _l +212 427 _l +228 461 250 488 276 506 _c +302 524 333 533 369 533 _c +426 533 468 516 495 484 _c +522 451 536 400 536 330 _c +536 52 _l +616 52 _l +616 0 _l +368 0 _l +368 52 _l +446 52 _l +446 302 _l +446 365 438 408 422 432 _c +406 456 379 468 340 468 _c +298 468 266 452 244 422 _c +222 391 212 347 212 289 _c +212 52 _l +290 52 _l +290 0 _l +41 0 _l +_cl}_e}_d +/o{602 0 50 -13 552 533 _sc +301 34 _m +349 34 385 53 410 91 _c +434 129 447 185 447 260 _c +447 334 434 390 410 428 _c +385 466 349 485 301 485 _c +253 485 216 466 192 428 _c +167 390 155 334 155 260 _c +155 185 167 129 192 91 _c +216 53 253 34 301 34 _c +301 -13 _m +225 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 72 408 118 458 _c +164 508 225 533 301 533 _c +377 533 437 508 483 458 _c +529 408 552 342 552 260 _c +552 177 529 111 483 61 _c +437 11 377 -13 301 -13 _c +_cl}_d +/r{478 0 36 0 478 533 _sc +478 520 _m +478 390 _l +426 390 _l +424 416 417 435 405 448 _c +392 460 373 467 349 467 _c +305 467 271 451 247 421 _c +223 390 212 346 212 289 _c +212 52 _l +316 52 _l +316 0 _l +41 0 _l +41 52 _l +122 52 _l +122 468 _l +36 468 _l +36 519 _l +212 519 _l +212 427 _l +229 463 251 489 279 507 _c +307 524 341 533 381 533 _c +395 533 411 531 427 529 _c +443 527 460 524 478 520 _c +_cl}_d +/s{{513 0 56 -13 462 533 _sc +56 29 _m +56 150 _l +108 150 _l +109 111 121 82 144 63 _c +167 43 201 34 246 34 _c +286 34 317 41 338 57 _c +359 72 370 94 370 123 _c +370 145 362 164 347 178 _c +331 192 299 207 249 223 _c +184 245 _l +139 259 107 277 87 299 _c +67 320 57 347 57 381 _c +57 428 74 465 109 492 _c +144 519 192 533 254 533 _c +281 533 310 529 340 522 _c +370 515 402 505 434 491 _c +434 378 _l +382 378 _l +380 411 369 437 347 456 _c +325 475 295 485 257 485 _c +219 485 190 478 171 465 _c +151 451 142 431 142 405 _c +142 383 149 365 164 352 _c +178 339 208 326 252 312 _c +323 290 _l +372 274 407 255 429 232 _c +451 209 462 180 462 144 _c +}_e{462 94 443 56 405 28 _c +367 0 316 -13 250 -13 _c +216 -13 184 -9 152 -3 _c +120 3 88 14 56 29 _c +_cl}_e}_d +/t{402 0 29 -13 394 680 _sc +108 467 _m +29 467 _l +29 519 _l +108 519 _l +108 680 _l +198 680 _l +198 519 _l +367 519 _l +367 467 _l +198 467 _l +198 137 _l +198 93 202 64 211 52 _c +219 40 235 34 258 34 _c +281 34 298 41 309 55 _c +319 69 325 91 326 122 _c +394 122 _l +391 74 378 40 355 19 _c +332 -2 297 -13 250 -13 _c +198 -13 161 -1 140 21 _c +118 43 108 82 108 137 _c +108 467 _l +_cl}_d +/u{{644 0 27 -13 607 519 _sc +354 519 _m +522 519 _l +522 52 _l +607 52 _l +607 0 _l +432 0 _l +432 92 _l +415 57 393 31 367 13 _c +341 -4 310 -13 276 -13 _c +218 -13 175 3 148 35 _c +121 67 108 119 108 189 _c +108 467 _l +27 467 _l +27 519 _l +198 519 _l +198 217 _l +198 153 205 110 221 87 _c +237 63 264 52 304 52 _c +346 52 377 67 399 98 _c +421 128 432 173 432 231 _c +432 467 _l +354 467 _l +354 519 _l +_cl}_e}_d +/v{565 0 -2 0 562 519 _sc +247 0 _m +56 467 _l +-2 467 _l +-2 519 _l +236 519 _l +236 467 _l +153 467 _l +299 110 _l +445 467 _l +367 467 _l +367 519 _l +562 519 _l +562 467 _l +504 467 _l +313 0 _l +247 0 _l +_cl}_d +/w{856 0 16 0 843 519 _sc +480 519 _m +613 114 _l +730 467 _l +655 467 _l +655 519 _l +843 519 _l +843 467 _l +785 467 _l +631 0 _l +556 0 _l +428 388 _l +300 0 _l +228 0 _l +74 467 _l +16 467 _l +16 519 _l +251 519 _l +251 467 _l +167 467 _l +283 114 _l +417 519 _l +480 519 _l +_cl}_d +end readonly def + +/BuildGlyph + {exch begin + CharStrings exch + 2 copy known not{pop /.notdef}if + true 3 1 roll get exec + end}_d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +}_d + +FontName currentdict end definefont pop +%% Font Page 00 +/BitstreamVeraSerif-Roman-ENC-00 [ +/.notdef/space/colon/C/l/i/e/n/t/A/g/M/a/r/parenleft/b/k/o/d/parenright/R/s/u +/c/h/I/D/B/v/O/w/Q/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef] def +/BitstreamVeraSerif-Roman-Uni-00 BitstreamVeraSerif-Roman-ENC-00 /BitstreamVeraSerif-Roman MFEmb +%%BeginFont: BitstreamVeraSerif-Roman +%!PS-AdobeFont-1.0 Composite Font +%%FontName: BitstreamVeraSerif-Roman-Uni +%%Creator: Composite font created by Qt +25 dict begin +/FontName /BitstreamVeraSerif-Roman-Uni def +/PaintType 0 def +/FontMatrix[1 0 0 1 0 0]def +/FontType 0 def +/FMapType 2 def +/Encoding [ +0]def +/FDepVector [ +/BitstreamVeraSerif-Roman-Uni-00 findfont +]def +FontName currentdict end definefont pop +%%EndFont +%%EndFont +/F1 8.33333/BitstreamVeraSerif-Roman-Uni DF +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +QI +%%EndPageSetup +[1 0 0 1 -42 -48]ST +0 0 B 0 0 PE +WB +W BC +42 48 597 168 R +2 0 255 0 0 0 0 PE +NB +67 216 69 VL +252 216 69 VL +432 202 69 VL +610 189 69 VL +0 0 B 0 0 PE +WB +243 100 17 32 R +255 0 0 P1 +NB +243 100 17 32 R +243 104 67 HL +1 255 0 0 BR +NP +242 104 MT +238 101 LT +238 107 LT +CP BF QS +71 126 67 129 DL +71 132 67 129 DL +3 0 255 0 0 0 0 PE +243 129 67 HL +0 0 B 0 0 PE +WB +423 101 17 32 R +255 0 0 P1 +NB +423 101 17 32 R +423 105 260 HL +1 255 0 0 BR +NP +422 105 MT +418 102 LT +418 108 LT +CP BF QS +264 127 260 130 DL +264 133 260 130 DL +3 0 255 0 0 0 0 PE +423 130 260 HL +255 0 0 P1 +NB +604 110 608 113 DL +604 116 608 113 DL +608 113 440 HL +255 0 0 P1 +437 162 433 165 DL +437 168 433 165 DL +608 165 433 HL +255 0 0 P1 +257 175 253 178 DL +257 181 253 178 DL +430 178 253 HL +255 0 0 P1 +72 189 68 192 DL +72 195 68 192 DL +250 192 68 HL +255 0 0 P1 +1 255 255 192 BR +42 48 50 21 R +CLSTART +5 5 40 11 ACR +CLEND +B P1 +F1 F +61 Y<000100020001000300040005000600070008>[3 0 3 0 3 0 6 0 3 0 3 0 5 0 5 0 0 0]34 50 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +255 0 0 P1 +1 255 255 192 BR +186 48 133 21 R +CLO +[1 0 0 1 -42 -48]ST +CLSTART +149 5 123 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<0001000200010009000A000600070008000B000C0007000C000A0006000D0001000E00040005000F000C001000110007000C001200050013>[3 0 3 0 3 0 6 0 5 0 5 0 5 0 3 0 8 0 5 0 5 0 5 0 5 0 5 0 4 0 3 0 3 0 3 0 3 0 5 0 5 0 5 0 5 0 5 0 5 0 5 0 3 0 0 0]123 191 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +255 0 0 P1 +1 255 255 192 BR +407 48 50 21 R +CLO +[1 0 0 1 -42 -48]ST +CLSTART +370 5 40 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<0001000200010003001100070008000D00110004>[3 0 3 0 3 0 6 0 5 0 5 0 3 0 4 0 5 0 0 0]40 412 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +255 0 0 P1 +1 255 255 192 BR +582 48 57 21 R +CLO +[1 0 0 1 -42 -48]ST +CLSTART +545 5 47 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<00010002000100140006001500110016000D00170006>[3 0 3 0 3 0 6 0 5 0 4 0 5 0 5 0 4 0 4 0 0 0]47 587 63 1 Tl XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +35 37 147 15 ACR +CLEND +50 d2 P1 +NB +F1 F +95 Y<000B0006000800180011001200010017000C00040004000200010017000D0006000C000800060009000A0006000700080019000700150008000C000700170006>[8 0 5 0 3 0 5 0 5 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 4 0 5 0 5 0 3 0 5 0 6 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 0 0]139 81 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +215 38 137 15 ACR +CLEND +50 d2 P1 +NB +F1 F +96 Y<001A001B0016001500010017000C00040004000200010017000D0006000C000800060009000A0006000700080019000700150008000C000700170006>[6 0 6 0 5 0 4 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 4 0 5 0 5 0 3 0 5 0 6 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 0 0]129 261 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +399 46 118 15 ACR +CLEND +50 d2 P1 +NB +F1 F +104 Y<0009001700080005001100070002000100150008000C000D0008001500010008001800060001000D0006001500110016000D00170006>[6 0 4 0 3 0 3 0 5 0 5 0 3 0 3 0 4 0 3 0 5 0 4 0 3 0 4 0 3 0 3 0 5 0 5 0 3 0 4 0 5 0 4 0 5 0 5 0 4 0 4 0 0 0]110 445 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +399 98 155 15 ACR +CLEND +50 d2 P1 +NB +F1 F +156 Y<001A001B00160015000100150005000A0007000C00040002000100150006000D001C000500170006001D001E00070006000D00030018000C0007000A00060012>[6 0 6 0 5 0 4 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 4 0 5 0 4 0 4 0 3 0 4 0 5 0 6 0 7 0 5 0 5 0 4 0 6 0 5 0 5 0 5 0 5 0 5 0 0 0]147 445 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +216 111 155 15 ACR +CLEND +50 d2 P1 +NB +F1 F +169 Y<001A001B00160015000100150005000A0007000C000400020001000C000A0006000700080019000700150008000C00070017000600090012001200060012>[6 0 6 0 5 0 4 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 5 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 5 0 6 0 5 0 5 0 5 0 0 0]138 266 XYT +CLO +[1 0 0 1 -42 -48]ST +CLSTART +39 125 134 15 ACR +CLEND +50 d2 P1 +NB +F1 F +183 Y<001F0008000100150005000A0007000C000400020001000C000A0006000700080019000700150008000C00070017000600090012001200060012>[6 0 3 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 5 0 5 0 5 0 5 0 3 0 3 0 5 0 4 0 3 0 5 0 5 0 4 0 5 0 6 0 5 0 5 0 5 0 0 0]126 85 XYT + +QP +%%Trailer +%%Pages: 1 +%%DocumentFonts: BitstreamVeraSerif-Roman +%%EOF diff --git a/doc/pics/akonadi_agent_handling.png b/doc/pics/akonadi_agent_handling.png new file mode 100644 index 00000000..ef14ec51 Binary files /dev/null and b/doc/pics/akonadi_agent_handling.png differ diff --git a/doc/pics/akonadi_agent_handling_small.png b/doc/pics/akonadi_agent_handling_small.png new file mode 100644 index 00000000..11678bdc Binary files /dev/null and b/doc/pics/akonadi_agent_handling_small.png differ diff --git a/doc/pics/akonadi_client_search.eps b/doc/pics/akonadi_client_search.eps new file mode 100644 index 00000000..ed6d8ecf --- /dev/null +++ b/doc/pics/akonadi_client_search.eps @@ -0,0 +1,1282 @@ +%!PS-Adobe-1.0 EPSF-3.0 +%%BoundingBox: 35 611 641 819 +%%Creator: Qt 3.3.6 +%%CreationDate: Do Sep 28 16:06:56 2006 +%%Orientation: Portrait +%%Pages: 1 +%%DocumentFonts: BitstreamVeraSerif-Roman + +%%EndComments +%%BeginProlog +% Prolog copyright 1994-2005 Trolltech. You may copy this prolog in any way +% that is directly related to this document. For other use of this prolog, +% see your licensing agreement for Qt. +/d/def load def/D{bind d}bind d/d2{dup dup}D/B{0 d2}D/W{255 d2}D/ED{exch d}D +/D0{0 ED}D/LT{lineto}D/MT{moveto}D/S{stroke}D/F{setfont}D/SW{setlinewidth}D +/CP{closepath}D/RL{rlineto}D/NP{newpath}D/CM{currentmatrix}D/SM{setmatrix}D +/TR{translate}D/SD{setdash}D/SC{aload pop setrgbcolor}D/CR{currentfile read +pop}D/i{index}D/bs{bitshift}D/scs{setcolorspace}D/DB{dict dup begin}D/DE{end +d}D/ie{ifelse}D/sp{astore pop}D/BSt 0 d/LWi 1 d/PSt 1 d/Cx 0 d/Cy 0 d/WFi +false d/OMo false d/BCol[1 1 1]d/PCol[0 0 0]d/BkCol[1 1 1]d/BDArr[0.94 0.88 +0.63 0.50 0.37 0.12 0.06]d/defM matrix d/nS 0 d/GPS{PSt 1 ge PSt 5 le and{{ +LArr PSt 1 sub 2 mul get}{LArr PSt 2 mul 1 sub get}ie}{[]}ie}D/QS{PSt 0 ne{ +gsave LWi SW true GPS 0 SD S OMo PSt 1 ne and{BkCol SC false GPS dup 0 get +SD S}if grestore}if}D/r28{{CR dup 32 gt{exit}if pop}loop 3{CR}repeat 0 4{7 +bs exch dup 128 gt{84 sub}if 42 sub 127 and add}repeat}D/rA 0 d/rL 0 d/rB{rL +0 eq{/rA r28 d/rL 28 d}if dup rL gt{rA exch rL sub rL exch/rA 0 d/rL 0 d rB +exch bs add}{dup rA 16#fffffff 3 -1 roll bs not and exch dup rL exch sub/rL +ED neg rA exch bs/rA ED}ie}D/uc{/rL 0 d 0{dup 2 i length ge{exit}if 1 rB 1 +eq{3 rB dup 3 ge{1 add dup rB 1 i 5 ge{1 i 6 ge{1 i 7 ge{1 i 8 ge{128 add}if +64 add}if 32 add}if 16 add}if 3 add exch pop}if 3 add exch 10 rB 1 add{dup 3 +i lt{dup}{2 i}ie 4 i 3 i 3 i sub 2 i getinterval 5 i 4 i 3 -1 roll +putinterval dup 4 -1 roll add 3 1 roll 4 -1 roll exch sub dup 0 eq{exit}if 3 +1 roll}loop pop pop}{3 rB 1 add{2 copy 8 rB put 1 add}repeat}ie}loop pop}D +/sl D0/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{/colorimage where{pop false 3 +colorimage}{exec/QCIcolor ED/QCIgray QCIcolor length 3 idiv string d 0 1 +QCIcolor length 3 idiv 1 sub{/QCIindex ED/x QCIindex 3 mul d QCIgray +QCIindex QCIcolor x get 0.30 mul QCIcolor x 1 add get 0.59 mul QCIcolor x 2 +add get 0.11 mul add add cvi put}for QCIgray image}ie}D/di{gsave TR 1 i 1 eq +{false eq{pop true 3 1 roll 4 i 4 i false 4 i 4 i imagemask BkCol SC +imagemask}{pop false 3 1 roll imagemask}ie}{dup false ne{/languagelevel +where{pop languagelevel 3 ge}{false}ie}{false}ie{/ma ED 8 eq{/dc[0 1]d +/DeviceGray}{/dc[0 1 0 1 0 1]d/DeviceRGB}ie scs/im ED/mt ED/h ED/w ED/id 7 +DB/ImageType 1 d/Width w d/Height h d/ImageMatrix mt d/DataSource im d +/BitsPerComponent 8 d/Decode dc d DE/md 7 DB/ImageType 1 d/Width w d/Height +h d/ImageMatrix mt d/DataSource ma d/BitsPerComponent 1 d/Decode[0 1]d DE 4 +DB/ImageType 3 d/DataDict id d/MaskDict md d/InterleaveType 3 d end image}{ +pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie grestore}d/BF{gsave BSt 1 eq{BCol SC +WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt 2 sub get/sc ED BCol{ +1. exch sub sc mul 1. exch sub}forall 3 array astore SC WFi{fill}{eofill}ie} +if BSt 9 ge BSt 14 le and{WFi{clip}{eoclip}ie defM SM pathbbox 3 i 3 i TR 4 +2 roll 3 2 roll exch sub/h ED sub/w ED OMo{NP 0 0 MT 0 h RL w 0 RL 0 h neg +RL CP BkCol SC fill}if BCol SC 0.3 SW NP BSt 9 eq BSt 11 eq or{0 4 h{dup 0 +exch MT w exch LT}for}if BSt 10 eq BSt 11 eq or{0 4 w{dup 0 MT h LT}for}if +BSt 12 eq BSt 14 eq or{w h gt{0 6 w h add{dup 0 MT h sub h LT}for}{0 6 w h +add{dup 0 exch MT w sub w exch LT}for}ie}if BSt 13 eq BSt 14 eq or{w h gt{0 +6 w h add{dup h MT h sub 0 LT}for}{0 6 w h add{dup w exch MT w sub 0 exch LT +}for}ie}if S}if BSt 24 eq{}if grestore}D/mat matrix d/ang1 D0/ang2 D0/w D0/h +D0/x D0/y D0/ARC{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED mat CM pop x w 2 div +add y h 2 div add TR 1 h w div neg scale ang2 0 ge{0 0 w 2 div ang1 ang1 +ang2 add arc}{0 0 w 2 div ang1 ang1 ang2 add arcn}ie mat SM}D/C D0/P{NP MT +0.5 0.5 rmoveto 0 -1 RL -1 0 RL 0 1 RL CP fill}D/M{/Cy ED/Cx ED}D/L{NP Cx Cy +MT/Cy ED/Cx ED Cx Cy LT QS}D/DL{NP MT LT QS}D/HL{1 i DL}D/VL{2 i exch DL}D/R +{/h ED/w ED/y ED/x ED NP x y MT 0 h RL w 0 RL 0 h neg RL CP BF QS}D/ACR{/h +ED/w ED/y ED/x ED x y MT 0 h RL w 0 RL 0 h neg RL CP}D/xr D0/yr D0/rx D0/ry +D0/rx2 D0/ry2 D0/RR{/yr ED/xr ED/h ED/w ED/y ED/x ED xr 0 le yr 0 le or{x y +w h R}{xr 100 ge yr 100 ge or{x y w h E}{/rx xr w mul 200 div d/ry yr h mul +200 div d/rx2 rx 2 mul d/ry2 ry 2 mul d NP x rx add y MT x y rx2 ry2 180 -90 +x y h add ry2 sub rx2 ry2 270 -90 x w add rx2 sub y h add ry2 sub rx2 ry2 0 +-90 x w add rx2 sub y rx2 ry2 90 -90 ARC ARC ARC ARC CP BF QS}ie}ie}D/E{/h +ED/w ED/y ED/x ED mat CM pop x w 2 div add y h 2 div add TR 1 h w div scale +NP 0 0 w 2 div 0 360 arc mat SM BF QS}D/A{16 div exch 16 div exch NP ARC QS} +D/PIE{/ang2 ED/ang1 ED/h ED/w ED/y ED/x ED NP x w 2 div add y h 2 div add MT +x y w h ang1 16 div ang2 16 div ARC CP BF QS}D/CH{16 div exch 16 div exch NP +ARC CP BF QS}D/BZ{curveto QS}D/CRGB{255 div 3 1 roll 255 div 3 1 roll 255 +div 3 1 roll}D/BC{CRGB BkCol sp}D/BR{CRGB BCol sp/BSt ED}D/WB{1 W BR}D/NB{0 +B BR}D/PE{setlinejoin setlinecap CRGB PCol sp/LWi ED/PSt ED LWi 0 eq{0.25 +/LWi ED}if PCol SC}D/P1{1 0 5 2 roll 0 0 PE}D/ST{defM SM concat}D/MF{true +exch true exch{exch pop exch pop dup 0 get dup findfont dup/FontName get 3 +-1 roll eq{exit}if}forall exch dup 1 get/fxscale ED 2 get/fslant ED exch +/fencoding ED[fxscale 0 fslant 1 0 0]makefont fencoding false eq{}{dup +maxlength dict begin{1 i/FID ne{def}{pop pop}ifelse}forall/Encoding +fencoding d currentdict end}ie definefont pop}D/MFEmb{findfont dup length +dict begin{1 i/FID ne{d}{pop pop}ifelse}forall/Encoding ED currentdict end +definefont pop}D/DF{findfont/fs 3 -1 roll d[fs 0 0 fs -1 mul 0 0]makefont d} +D/ty 0 d/Y{/ty ED}D/Tl{gsave SW NP 1 i exch MT 1 i 0 RL S grestore}D/XYT{ty +MT/xyshow where{pop pop xyshow}{exch pop 1 i dup length 2 div exch +stringwidth pop 3 -1 roll exch sub exch div exch 0 exch ashow}ie}D/AT{ty MT +1 i dup length 2 div exch stringwidth pop 3 -1 roll exch sub exch div exch 0 +exch ashow}D/QI{/C save d pageinit/Cx 0 d/Cy 0 d/OMo false d}D/QP{C restore +showpage}D/SPD{/setpagedevice where{1 DB 3 1 roll d end setpagedevice}{pop +pop}ie}D/SV{BSt LWi PSt Cx Cy WFi OMo BCol PCol BkCol/nS nS 1 add d gsave}D +/RS{nS 0 gt{grestore/BkCol ED/PCol ED/BCol ED/OMo ED/WFi ED/Cy ED/Cx ED/PSt +ED/LWi ED/BSt ED/nS nS 1 sub d}if}D/CLSTART{/clipTmp matrix CM d defM SM NP} +D/CLEND{clip NP clipTmp SM}D/CLO{grestore gsave defM SM}D + +/LArr[ [] [] [ 10.417 3.125 ] [ 3.125 10.417 ] [ 3.125 3.125 ] [ 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 ] [ 5.208 3.125 3.125 3.125 3.125 ] [ 3.125 5.208 3.125 3.125 3.125 3.125 ] ] d +/pageinit { +35.52 24 translate +% 185*280mm (portrait) +0 793.92 translate 0.96 -0.96 scale/defM matrix CM d } d +%%EndProlog +%%BeginSetup +% Fonts and encodings used +/BitstreamVeraSerif-RomanList [ +[ /BitstreamVeraSerif-Roman 1.0 0.0 ] + [ /BitstreamVeraSerif 1.0 0.0 ] + [ /Helvetica 0.988 0.000 ] +] d +%%BeginFont: Bitstream Vera Serif +%!PS-Adobe-3.0 Resource-Font +%%Copyright: Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. +%%Creator: Converted from TrueType by Qt +25 dict begin +/_d{bind def}bind def +/_m{moveto}_d +/_l{lineto}_d +/_cl{closepath eofill}_d +/_c{curveto}_d +/_sc{7 -1 roll{setcachedevice}{pop pop pop pop pop pop}ifelse}_d +/_e{exec}_d +/FontName /BitstreamVeraSerif-Roman def +/PaintType 0 def +/FontMatrix[.001 0 0 .001 0 0]def +/FontBBox[-182 -235 1287 928]def +/FontType 3 def +/Encoding StandardEncoding def +/FontInfo 10 dict dup begin +/FamilyName (Bitstream Vera Serif) def +/FullName (Bitstream Vera Serif) def +/Notice (Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc.) def +/Weight (Roman) def +/Version (Release 1.10) def +/ItalicAngle 0.0 def +/isFixedPitch false def +/UnderlinePosition -213 def +/UnderlineThickness 133 def +end readonly def +/CharStrings 33 dict dup begin +/.notdef{600 0 50 -176 550 705 _sc +50 -176 _m +50 705 _l +550 705 _l +550 -176 _l +50 -176 _l +106 -120 _m +494 -120 _l +494 649 _l +106 649 _l +106 -120 _l +_cl}_d +/space{318 0 0 0 0 0 _sc +}_d +/colon{337 0 104 -13 234 434 _sc +104 51 _m +104 69 110 84 123 97 _c +135 109 151 116 169 116 _c +187 116 202 109 215 97 _c +227 84 234 69 234 51 _c +234 32 227 17 215 5 _c +203 -7 187 -13 169 -13 _c +150 -13 134 -7 122 5 _c +110 17 104 32 104 51 _c +104 369 _m +104 387 110 402 123 415 _c +135 427 151 434 169 434 _c +187 434 203 427 215 415 _c +227 403 234 387 234 369 _c +234 350 227 334 215 322 _c +203 310 187 304 169 304 _c +151 304 135 310 123 323 _c +110 335 104 351 104 369 _c +_cl}_d +/A{722 0 -5 0 732 729 _sc +200 264 _m +468 264 _l +334 611 _l +200 264 _l +-5 0 _m +-5 52 _l +58 52 _l +318 729 _l +400 729 _l +660 52 _l +732 52 _l +732 0 _l +467 0 _l +467 52 _l +548 52 _l +487 212 _l +180 212 _l +119 52 _l +199 52 _l +199 0 _l +-5 0 _l +_cl}_d +/B{{735 0 55 0 674 729 _sc +247 52 _m +393 52 _l +451 52 494 64 521 90 _c +548 115 562 155 562 211 _c +562 265 548 305 521 331 _c +494 356 451 369 393 369 _c +247 369 _l +247 52 _l +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +415 729 _l +488 729 543 714 581 684 _c +618 654 637 609 637 549 _c +637 505 624 471 598 445 _c +572 419 535 404 485 398 _c +547 390 594 370 626 338 _c +658 306 674 264 674 211 _c +674 139 651 85 605 51 _c +559 17 488 0 392 0 _c +55 0 _l +247 421 _m +371 421 _l +424 421 463 431 488 451 _c +512 471 525 504 525 549 _c +}_e{525 593 512 626 488 646 _c +463 666 424 677 371 677 _c +247 677 _l +247 421 _l +_cl}_e}_d +/C{{765 0 56 -13 705 742 _sc +705 193 _m +683 125 647 73 597 39 _c +546 4 482 -13 405 -13 _c +357 -13 312 -5 272 11 _c +231 27 195 50 164 82 _c +127 118 100 160 82 206 _c +64 252 56 305 56 364 _c +56 477 88 568 154 638 _c +219 707 305 742 413 742 _c +453 742 495 736 540 726 _c +584 716 633 700 685 679 _c +685 511 _l +630 511 _l +618 572 593 617 557 646 _c +521 675 470 690 405 690 _c +327 690 268 662 228 607 _c +188 551 168 470 168 364 _c +168 257 188 176 228 121 _c +268 65 327 38 405 38 _c +459 38 503 51 539 77 _c +574 103 599 141 615 193 _c +705 193 _l +_cl}_e}_d +/D{802 0 55 0 744 729 _sc +247 52 _m +338 52 _l +432 52 505 79 556 133 _c +606 187 632 264 632 365 _c +632 466 606 543 556 597 _c +505 650 432 677 338 677 _c +247 677 _l +247 52 _l +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +345 729 _l +471 729 569 697 639 633 _c +709 569 744 479 744 365 _c +744 250 708 160 638 96 _c +568 32 470 0 345 0 _c +55 0 _l +_cl}_d +/E{730 0 55 0 650 729 _sc +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +642 729 _l +642 567 _l +582 567 _l +582 669 _l +247 669 _l +247 425 _l +486 425 _l +486 516 _l +546 516 _l +546 274 _l +486 274 _l +486 365 _l +247 365 _l +247 60 _l +590 60 _l +590 162 _l +650 162 _l +650 0 _l +55 0 _l +_cl}_d +/I{395 0 55 0 340 729 _sc +247 52 _m +340 52 _l +340 0 _l +55 0 _l +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +340 729 _l +340 677 _l +247 677 _l +247 52 _l +_cl}_d +/M{1024 0 50 0 973 729 _sc +55 0 _m +55 52 _l +148 52 _l +148 677 _l +50 677 _l +50 729 _l +262 729 _l +518 210 _l +774 729 _l +973 729 _l +973 677 _l +876 677 _l +876 52 _l +969 52 _l +969 0 _l +684 0 _l +684 52 _l +777 52 _l +777 615 _l +527 107 _l +458 107 _l +208 615 _l +208 52 _l +301 52 _l +301 0 _l +55 0 _l +_cl}_d +/P{{673 0 55 0 637 729 _sc +247 371 _m +376 371 _l +424 371 461 384 487 410 _c +512 436 525 474 525 524 _c +525 574 512 612 487 638 _c +461 664 424 677 376 677 _c +247 677 _l +247 371 _l +55 0 _m +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +400 729 _l +472 729 530 710 573 673 _c +615 636 637 586 637 524 _c +637 461 615 411 573 374 _c +530 337 472 319 400 319 _c +247 319 _l +247 52 _l +360 52 _l +360 0 _l +55 0 _l +_cl}_e}_d +/R{{753 0 55 0 777 729 _sc +479 362 _m +501 356 521 345 538 330 _c +554 315 569 294 582 268 _c +688 52 _l +777 52 _l +777 0 _l +605 0 _l +491 232 _l +469 276 449 305 431 319 _c +413 332 388 339 356 339 _c +247 339 _l +247 52 _l +350 52 _l +350 0 _l +55 0 _l +55 52 _l +148 52 _l +148 677 _l +55 677 _l +55 729 _l +425 729 _l +495 729 550 712 589 678 _c +627 644 647 596 647 534 _c +647 484 633 444 605 416 _c +577 387 535 369 479 362 _c +247 391 _m +391 391 _l +440 391 476 402 500 426 _c +523 449 535 485 535 534 _c +}_e{535 582 523 618 500 642 _c +476 665 440 677 391 677 _c +247 677 _l +247 391 _l +_cl}_e}_d +/S{{685 0 84 -13 612 742 _sc +93 35 _m +93 201 _l +149 201 _l +150 145 166 104 197 78 _c +227 51 274 38 336 38 _c +394 38 438 49 468 72 _c +498 95 514 129 514 173 _c +514 208 504 235 486 254 _c +468 272 429 291 370 309 _c +274 338 _l +204 359 154 385 126 417 _c +98 448 84 491 84 547 _c +84 609 106 657 150 691 _c +194 725 255 742 335 742 _c +369 742 406 738 446 731 _c +486 723 529 713 575 699 _c +575 544 _l +520 544 _l +514 595 497 632 468 655 _c +439 678 395 690 337 690 _c +285 690 246 679 219 658 _c +192 637 179 607 179 567 _c +179 532 189 505 209 485 _c +229 465 272 445 338 426 _c +428 399 _l +494 379 541 353 569 323 _c +597 292 612 251 612 199 _c +}_e{612 128 589 75 544 40 _c +498 4 431 -13 342 -13 _c +302 -13 261 -9 219 -1 _c +177 6 135 18 93 35 _c +_cl}_e}_d +/a{{596 0 50 -13 568 533 _sc +398 163 _m +398 273 _l +282 273 _l +237 273 204 263 182 244 _c +160 224 150 195 150 156 _c +150 120 161 91 183 70 _c +205 48 235 38 273 38 _c +310 38 340 49 363 72 _c +386 95 398 125 398 163 _c +488 324 _m +488 52 _l +568 52 _l +568 0 _l +398 0 _l +398 56 _l +378 32 355 14 329 3 _c +303 -7 272 -13 238 -13 _c +180 -13 134 2 100 32 _c +66 62 50 104 50 156 _c +50 209 69 250 108 280 _c +146 310 201 325 272 325 _c +398 325 _l +398 361 _l +398 400 386 430 362 452 _c +338 474 304 485 261 485 _c +225 485 197 476 176 460 _c +154 444 141 420 136 388 _c +90 388 _l +}_e{90 493 _l +121 506 151 516 181 523 _c +210 529 239 533 267 533 _c +339 533 393 515 431 479 _c +469 443 488 392 488 324 _c +_cl}_e}_d +/c{{560 0 50 -13 514 533 _sc +514 156 _m +501 100 477 58 441 30 _c +405 1 358 -13 301 -13 _c +225 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 73 408 119 458 _c +165 508 225 533 301 533 _c +333 533 366 529 399 521 _c +431 513 464 502 497 487 _c +497 354 _l +445 354 _l +438 399 423 432 400 453 _c +377 474 344 485 302 485 _c +253 485 216 466 192 428 _c +167 390 155 334 155 260 _c +155 184 167 128 192 90 _c +216 52 253 34 302 34 _c +340 34 371 44 394 64 _c +417 84 433 115 442 156 _c +514 156 _l +_cl}_e}_d +/d{{640 0 50 -13 611 760 _sc +525 52 _m +611 52 _l +611 0 _l +435 0 _l +435 81 _l +417 48 395 24 368 9 _c +340 -5 307 -13 267 -13 _c +203 -13 150 12 110 62 _c +70 112 50 178 50 260 _c +50 341 70 407 110 457 _c +150 507 203 533 267 533 _c +307 533 340 525 368 510 _c +395 494 417 470 435 438 _c +435 708 _l +350 708 _l +350 760 _l +525 760 _l +525 52 _l +435 234 _m +435 285 _l +435 347 423 394 399 427 _c +375 460 340 477 295 477 _c +249 477 214 458 190 422 _c +166 386 155 332 155 260 _c +155 188 166 133 190 97 _c +214 60 249 42 295 42 _c +340 42 375 58 399 91 _c +423 123 435 171 435 234 _c +}_e{_cl}_e}_d +/e{{592 0 50 -13 542 533 _sc +542 250 _m +155 250 _l +155 246 _l +155 176 168 123 194 87 _c +220 51 259 34 311 34 _c +350 34 382 44 408 65 _c +433 85 451 116 461 157 _c +533 157 _l +519 100 492 57 454 29 _c +415 1 364 -13 302 -13 _c +226 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 72 408 118 458 _c +163 508 222 533 296 533 _c +374 533 435 508 477 460 _c +519 412 540 342 542 250 _c +436 302 _m +434 362 421 408 397 439 _c +373 469 340 485 296 485 _c +254 485 222 469 198 438 _c +174 407 160 362 155 302 _c +436 302 _l +_cl}_e}_d +/f{{370 0 36 0 430 760 _sc +430 637 _m +383 637 _l +382 661 375 680 362 693 _c +348 705 329 712 303 712 _c +269 712 246 702 232 684 _c +218 666 212 633 212 586 _c +212 519 _l +357 519 _l +357 467 _l +212 467 _l +212 52 _l +327 52 _l +327 0 _l +36 0 _l +36 52 _l +122 52 _l +122 467 _l +36 467 _l +36 519 _l +122 519 _l +122 584 _l +122 642 137 685 167 715 _c +197 745 241 760 300 760 _c +322 760 343 758 365 754 _c +387 750 408 744 430 736 _c +430 637 _l +_cl}_e}_d +/g{{640 0 50 -221 611 533 _sc +525 467 _m +525 11 _l +525 -63 504 -120 463 -160 _c +422 -200 364 -221 288 -221 _c +254 -221 221 -218 190 -212 _c +158 -206 128 -196 100 -184 _c +100 -75 _l +147 -75 _l +153 -109 166 -133 188 -149 _c +210 -165 241 -173 282 -173 _c +334 -173 373 -158 398 -128 _c +422 -98 435 -51 435 11 _c +435 81 _l +417 48 395 24 368 9 _c +340 -5 307 -13 267 -13 _c +203 -13 150 12 110 62 _c +70 112 50 178 50 260 _c +50 341 70 407 110 457 _c +150 507 203 533 267 533 _c +307 533 340 525 368 510 _c +395 494 417 470 435 438 _c +435 519 _l +611 519 _l +611 467 _l +525 467 _l +435 285 _m +}_e{435 347 423 394 399 427 _c +375 460 340 477 295 477 _c +249 477 214 458 190 422 _c +166 386 155 332 155 260 _c +155 188 166 133 190 97 _c +214 60 249 42 295 42 _c +340 42 375 58 399 91 _c +423 123 435 171 435 234 _c +435 285 _l +_cl}_e}_d +/h{{644 0 36 0 616 760 _sc +41 0 _m +41 52 _l +122 52 _l +122 708 _l +36 708 _l +36 760 _l +212 760 _l +212 427 _l +228 461 250 488 276 506 _c +302 524 333 533 369 533 _c +426 533 468 516 495 484 _c +522 451 536 400 536 330 _c +536 52 _l +616 52 _l +616 0 _l +368 0 _l +368 52 _l +446 52 _l +446 302 _l +446 365 438 408 422 432 _c +406 455 379 467 340 467 _c +298 467 266 451 244 421 _c +222 391 212 347 212 289 _c +212 52 _l +290 52 _l +290 0 _l +41 0 _l +_cl}_e}_d +/i{320 0 36 0 297 736 _sc +97 680 _m +97 695 102 708 113 719 _c +124 730 137 736 153 736 _c +167 736 180 730 191 719 _c +202 708 208 695 208 680 _c +208 664 202 651 192 641 _c +181 630 168 625 153 625 _c +137 625 124 630 113 641 _c +102 651 97 664 97 680 _c +212 52 _m +297 52 _l +297 0 _l +36 0 _l +36 52 _l +122 52 _l +122 467 _l +36 467 _l +36 519 _l +212 519 _l +212 52 _l +_cl}_d +/l{320 0 29 0 290 760 _sc +205 52 _m +290 52 _l +290 0 _l +29 0 _l +29 52 _l +115 52 _l +115 708 _l +29 708 _l +29 760 _l +205 760 _l +205 52 _l +_cl}_d +/m{{948 0 36 0 921 533 _sc +518 418 _m +535 456 557 484 584 504 _c +611 523 642 533 678 533 _c +732 533 773 516 800 482 _c +826 448 840 398 840 330 _c +840 52 _l +921 52 _l +921 0 _l +672 0 _l +672 52 _l +750 52 _l +750 320 _l +750 372 742 410 726 433 _c +710 455 685 467 649 467 _c +609 467 578 451 557 421 _c +536 391 526 347 526 289 _c +526 52 _l +604 52 _l +604 0 _l +358 0 _l +358 52 _l +436 52 _l +436 323 _l +436 375 428 412 412 434 _c +396 456 371 467 335 467 _c +295 467 264 451 243 421 _c +222 391 212 347 212 289 _c +212 52 _l +}_e{290 52 _l +290 0 _l +41 0 _l +41 52 _l +122 52 _l +122 468 _l +36 468 _l +36 519 _l +212 519 _l +212 427 _l +228 461 249 488 275 506 _c +301 524 330 533 363 533 _c +403 533 436 523 463 503 _c +490 483 508 454 518 418 _c +_cl}_e}_d +/n{{644 0 36 0 616 533 _sc +41 0 _m +41 52 _l +122 52 _l +122 467 _l +36 467 _l +36 519 _l +212 519 _l +212 427 _l +228 461 250 488 276 506 _c +302 524 333 533 369 533 _c +426 533 468 516 495 484 _c +522 451 536 400 536 330 _c +536 52 _l +616 52 _l +616 0 _l +368 0 _l +368 52 _l +446 52 _l +446 302 _l +446 365 438 408 422 432 _c +406 456 379 468 340 468 _c +298 468 266 452 244 422 _c +222 391 212 347 212 289 _c +212 52 _l +290 52 _l +290 0 _l +41 0 _l +_cl}_e}_d +/o{602 0 50 -13 552 533 _sc +301 34 _m +349 34 385 53 410 91 _c +434 129 447 185 447 260 _c +447 334 434 390 410 428 _c +385 466 349 485 301 485 _c +253 485 216 466 192 428 _c +167 390 155 334 155 260 _c +155 185 167 129 192 91 _c +216 53 253 34 301 34 _c +301 -13 _m +225 -13 165 11 119 61 _c +73 111 50 177 50 260 _c +50 342 72 408 118 458 _c +164 508 225 533 301 533 _c +377 533 437 508 483 458 _c +529 408 552 342 552 260 _c +552 177 529 111 483 61 _c +437 11 377 -13 301 -13 _c +_cl}_d +/q{{640 0 50 -207 611 533 _sc +525 467 _m +525 -155 _l +611 -155 _l +611 -207 _l +350 -207 _l +350 -155 _l +435 -155 _l +435 81 _l +417 48 395 24 368 9 _c +340 -5 307 -13 267 -13 _c +203 -13 150 12 110 62 _c +70 112 50 178 50 260 _c +50 341 70 407 110 457 _c +150 507 203 533 267 533 _c +307 533 340 525 368 510 _c +395 494 417 470 435 438 _c +435 519 _l +611 519 _l +611 467 _l +525 467 _l +435 285 _m +435 347 423 394 399 427 _c +375 460 340 477 295 477 _c +249 477 214 458 190 422 _c +166 386 155 332 155 260 _c +155 188 166 133 190 97 _c +214 60 249 42 295 42 _c +}_e{340 42 375 58 399 91 _c +423 123 435 171 435 234 _c +435 285 _l +_cl}_e}_d +/r{478 0 36 0 478 533 _sc +478 520 _m +478 390 _l +426 390 _l +424 416 417 435 405 448 _c +392 460 373 467 349 467 _c +305 467 271 451 247 421 _c +223 390 212 346 212 289 _c +212 52 _l +316 52 _l +316 0 _l +41 0 _l +41 52 _l +122 52 _l +122 468 _l +36 468 _l +36 519 _l +212 519 _l +212 427 _l +229 463 251 489 279 507 _c +307 524 341 533 381 533 _c +395 533 411 531 427 529 _c +443 527 460 524 478 520 _c +_cl}_d +/s{{513 0 56 -13 462 533 _sc +56 29 _m +56 150 _l +108 150 _l +109 111 121 82 144 63 _c +167 43 201 34 246 34 _c +286 34 317 41 338 57 _c +359 72 370 94 370 123 _c +370 145 362 164 347 178 _c +331 192 299 207 249 223 _c +184 245 _l +139 259 107 277 87 299 _c +67 320 57 347 57 381 _c +57 428 74 465 109 492 _c +144 519 192 533 254 533 _c +281 533 310 529 340 522 _c +370 515 402 505 434 491 _c +434 378 _l +382 378 _l +380 411 369 437 347 456 _c +325 475 295 485 257 485 _c +219 485 190 478 171 465 _c +151 451 142 431 142 405 _c +142 383 149 365 164 352 _c +178 339 208 326 252 312 _c +323 290 _l +372 274 407 255 429 232 _c +451 209 462 180 462 144 _c +}_e{462 94 443 56 405 28 _c +367 0 316 -13 250 -13 _c +216 -13 184 -9 152 -3 _c +120 3 88 14 56 29 _c +_cl}_e}_d +/t{402 0 29 -13 394 680 _sc +108 467 _m +29 467 _l +29 519 _l +108 519 _l +108 680 _l +198 680 _l +198 519 _l +367 519 _l +367 467 _l +198 467 _l +198 137 _l +198 93 202 64 211 52 _c +219 40 235 34 258 34 _c +281 34 298 41 309 55 _c +319 69 325 91 326 122 _c +394 122 _l +391 74 378 40 355 19 _c +332 -2 297 -13 250 -13 _c +198 -13 161 -1 140 21 _c +118 43 108 82 108 137 _c +108 467 _l +_cl}_d +/u{{644 0 27 -13 607 519 _sc +354 519 _m +522 519 _l +522 52 _l +607 52 _l +607 0 _l +432 0 _l +432 92 _l +415 57 393 31 367 13 _c +341 -4 310 -13 276 -13 _c +218 -13 175 3 148 35 _c +121 67 108 119 108 189 _c +108 467 _l +27 467 _l +27 519 _l +198 519 _l +198 217 _l +198 153 205 110 221 87 _c +237 63 264 52 304 52 _c +346 52 377 67 399 98 _c +421 128 432 173 432 231 _c +432 467 _l +354 467 _l +354 519 _l +_cl}_e}_d +/v{565 0 -2 0 562 519 _sc +247 0 _m +56 467 _l +-2 467 _l +-2 519 _l +236 519 _l +236 467 _l +153 467 _l +299 110 _l +445 467 _l +367 467 _l +367 519 _l +562 519 _l +562 467 _l +504 467 _l +313 0 _l +247 0 _l +_cl}_d +/x{{564 0 12 0 552 519 _sc +291 317 _m +400 467 _l +330 467 _l +330 519 _l +530 519 _l +530 467 _l +461 467 _l +322 275 _l +484 52 _l +552 52 _l +552 0 _l +312 0 _l +312 52 _l +378 52 _l +265 207 _l +152 52 _l +219 52 _l +219 0 _l +22 0 _l +22 52 _l +91 52 _l +234 249 _l +76 467 _l +12 467 _l +12 519 _l +244 519 _l +244 467 _l +182 467 _l +291 317 _l +_cl}_e}_d +/y{{565 0 -2 -221 562 519 _sc +216 -94 _m +250 -8 _l +56 467 _l +-2 467 _l +-2 519 _l +236 519 _l +236 467 _l +153 467 _l +299 110 _l +445 467 _l +367 467 _l +367 519 _l +562 519 _l +562 467 _l +504 467 _l +266 -116 _l +250 -156 232 -184 212 -199 _c +192 -213 164 -221 128 -221 _c +112 -221 97 -219 81 -217 _c +65 -214 48 -210 32 -206 _c +32 -107 _l +78 -107 _l +80 -129 85 -144 95 -154 _c +104 -164 118 -169 138 -169 _c +156 -169 170 -164 181 -154 _c +192 -144 204 -124 216 -94 _c +_cl}_e}_d +end readonly def + +/BuildGlyph + {exch begin + CharStrings exch + 2 copy known not{pop /.notdef}if + true 3 1 roll get exec + end}_d + +/BuildChar { + 1 index /Encoding get exch get + 1 index /BuildGlyph get exec +}_d + +FontName currentdict end definefont pop +%% Font Page 00 +/BitstreamVeraSerif-Roman-ENC-00 [ +/.notdef/space/colon/C/l/i/e/n/t/S/o/r/a/g/R/s/u/c/h/P/v/d/E/x/D/B/I/m/q/y/M +/A/f/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef +/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef] def +/BitstreamVeraSerif-Roman-Uni-00 BitstreamVeraSerif-Roman-ENC-00 /BitstreamVeraSerif-Roman MFEmb +%%BeginFont: BitstreamVeraSerif-Roman +%!PS-AdobeFont-1.0 Composite Font +%%FontName: BitstreamVeraSerif-Roman-Uni +%%Creator: Composite font created by Qt +25 dict begin +/FontName /BitstreamVeraSerif-Roman-Uni def +/PaintType 0 def +/FontMatrix[1 0 0 1 0 0]def +/FontType 0 def +/FMapType 2 def +/Encoding [ +0]def +/FDepVector [ +/BitstreamVeraSerif-Roman-Uni-00 findfont +]def +FontName currentdict end definefont pop +%%EndFont +%%EndFont +/F1 8.33333/BitstreamVeraSerif-Roman-Uni DF +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +QI +%%EndPageSetup +[1 0 0 1 -39 -48]ST +0 0 B 0 0 PE +WB +W BC +39 48 630 215 R +2 0 255 0 0 0 0 PE +NB +72 263 69 VL +353 263 69 VL +500 163 69 VL +228 195 69 VL +615 159 69 VL +255 0 0 P1 +222 87 226 90 DL +222 93 226 90 DL +226 90 73 HL +0 0 B 0 0 PE +WB +491 111 17 32 R +255 0 0 P1 +NB +491 111 17 32 R +491 115 353 HL +1 255 0 0 BR +NP +490 115 MT +486 112 LT +486 118 LT +CP BF QS +357 137 353 140 DL +357 143 353 140 DL +3 0 255 0 0 0 0 PE +491 140 353 HL +255 0 0 P1 +NB +77 168 73 171 DL +77 174 73 171 DL +226 171 73 HL +0 0 B 0 0 PE +WB +344 112 17 32 R +255 0 0 P1 +NB +344 112 17 32 R +344 116 228 HL +1 255 0 0 BR +NP +343 116 MT +339 113 LT +339 119 LT +CP BF QS +232 138 228 141 DL +232 144 228 141 DL +3 0 255 0 0 0 0 PE +344 141 228 HL +255 0 0 P1 +NB +609 112 613 115 DL +609 118 613 115 DL +613 115 508 HL +255 0 0 P1 +505 132 501 135 DL +505 138 501 135 DL +613 135 501 HL +255 0 0 P1 +358 132 354 135 DL +358 138 354 135 DL +491 135 354 HL +0 0 B 0 0 PE +WB +344 211 17 32 R +255 0 0 P1 +NB +344 211 17 32 R +344 215 72 HL +1 255 0 0 BR +NP +343 215 MT +339 212 LT +339 218 LT +CP BF QS +76 237 72 240 DL +76 243 72 240 DL +3 0 255 0 0 0 0 PE +344 240 72 HL +255 0 0 P1 +1 255 255 192 BR +39 48 66 21 R +CLSTART +5 5 56 11 ACR +CLEND +B P1 +F1 F +61 Y<000100020001000300040005000600070008>[3 0 3 0 3 0 6 0 3 0 3 0 5 0 5 0 0 0]34 55 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +328 48 51 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +294 5 41 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<00010002000100090008000A000B000C000D0006>[3 0 3 0 3 0 5 0 3 0 5 0 4 0 5 0 5 0 0 0]41 333 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +472 48 57 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +438 5 47 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<000100020001000E0006000F000A0010000B00110006>[3 0 3 0 3 0 6 0 5 0 4 0 5 0 5 0 4 0 4 0 0 0]47 477 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +186 48 85 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +152 5 75 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<00010002000100090006000C000B0011001200010013000B000A0014000500150006000B>[3 0 3 0 3 0 5 0 5 0 5 0 4 0 4 0 5 0 3 0 5 0 4 0 5 0 4 0 3 0 5 0 5 0 0 0]75 191 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +255 0 0 P1 +1 255 255 192 BR +562 48 107 21 R +CLO +[1 0 0 1 -39 -48]ST +CLSTART +528 5 97 11 ACR +CLEND +B P1 +1 255 255 192 BR +F1 F +<0001000200010016001700080006000B0007000C000400010018000C0008000C00010009000A0010000B00110006>[3 0 3 0 3 0 6 0 4 0 3 0 5 0 4 0 5 0 5 0 3 0 3 0 6 0 5 0 3 0 5 0 3 0 5 0 5 0 5 0 4 0 4 0 0 0]97 567 63 1 Tl XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +39 23 102 15 ACR +CLEND +50 d2 P1 +NB +F1 F +81 Y<001800190010000F00010011000C0004000400020001000F0006000C000B00110012001A00080006001B000F>[6 0 6 0 5 0 4 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 5 0 5 0 4 0 4 0 5 0 3 0 3 0 5 0 7 0 0 0]94 82 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +319 48 136 15 ACR +CLEND +50 d2 P1 +NB +F1 F +106 Y<001800190010000F00010011000C0004000400020001000B0006001C00100006000F0008001A00080006001B001800060004000500140006000B001D>[6 0 6 0 5 0 4 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 5 0 5 0 5 0 5 0 4 0 3 0 3 0 3 0 5 0 7 0 6 0 5 0 3 0 3 0 4 0 5 0 4 0 0 0]128 362 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +39 104 138 15 ACR +CLEND +50 d2 P1 +NB +F1 F +162 Y<001800190010000F0001000F0005000D0007000C000400020001000F0006000C000B00110012001A00080006001B000F000E0006000F001000040008>[6 0 6 0 5 0 4 0 3 0 4 0 3 0 5 0 5 0 5 0 3 0 3 0 3 0 4 0 5 0 5 0 4 0 4 0 5 0 3 0 3 0 5 0 7 0 4 0 6 0 5 0 4 0 5 0 3 0 0 0]130 82 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +198 49 74 15 ACR +CLEND +50 d2 P1 +NB +F1 F +107 Y<001A001E001F001300010011000C000400040002000100200006000800110012>[3 0 8 0 6 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 3 0 5 0 3 0 4 0 0 0]66 241 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +467 48 92 15 ACR +CLEND +50 d2 P1 +NB +F1 F +106 Y<001F001100080005000A000700020001000B0006001C00100006000F000800010015000C0008000C>[6 0 4 0 3 0 3 0 5 0 5 0 3 0 3 0 4 0 5 0 5 0 5 0 5 0 4 0 3 0 3 0 5 0 5 0 3 0 0 0]84 510 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +467 68 94 15 ACR +CLEND +50 d2 P1 +NB +F1 F +126 Y<001F001100080005000A000700020001000B00060008000B000500060014000600010015000C0008000C>[6 0 4 0 3 0 3 0 5 0 5 0 3 0 3 0 4 0 5 0 3 0 4 0 3 0 5 0 4 0 5 0 3 0 5 0 5 0 3 0 0 0]86 510 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +320 68 75 15 ACR +CLEND +50 d2 P1 +NB +F1 F +<001A001E001F001300010011000C0004000400020001000F0008000A000B0006>[3 0 8 0 6 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 4 0 3 0 5 0 4 0 0 0]67 363 XYT +CLO +[1 0 0 1 -39 -48]ST +CLSTART +38 148 74 15 ACR +CLEND +50 d2 P1 +NB +F1 F +206 Y<001A001E001F001300010011000C000400040002000100200006000800110012>[3 0 8 0 6 0 5 0 3 0 4 0 5 0 3 0 3 0 3 0 3 0 3 0 5 0 3 0 4 0 0 0]66 81 XYT + +QP +%%Trailer +%%Pages: 1 +%%DocumentFonts: BitstreamVeraSerif-Roman +%%EOF diff --git a/doc/pics/akonadi_client_search.png b/doc/pics/akonadi_client_search.png new file mode 100644 index 00000000..1c5915af Binary files /dev/null and b/doc/pics/akonadi_client_search.png differ diff --git a/doc/pics/akonadi_client_search_small.png b/doc/pics/akonadi_client_search_small.png new file mode 100644 index 00000000..c089a302 Binary files /dev/null and b/doc/pics/akonadi_client_search_small.png differ diff --git a/doc/pics/akonadi_communication.xmi b/doc/pics/akonadi_communication.xmi new file mode 100644 index 00000000..ba88a2a5 --- /dev/null +++ b/doc/pics/akonadi_communication.xmi @@ -0,0 +1,185 @@ + + + + + umbrello uml modeller http://uml.sf.net + 1.5.3 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/pics/akonadi_concept_schema.sla b/doc/pics/akonadi_concept_schema.sla new file mode 100644 index 00000000..9ffc59b6 --- /dev/null +++ b/doc/pics/akonadi_concept_schema.sla @@ -0,0 +1,721 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/pics/akonadi_overview_uml.png b/doc/pics/akonadi_overview_uml.png new file mode 100644 index 00000000..70817a61 Binary files /dev/null and b/doc/pics/akonadi_overview_uml.png differ diff --git a/doc/pics/akonadi_overview_uml.ps b/doc/pics/akonadi_overview_uml.ps new file mode 100644 index 00000000..91f3cc0a --- /dev/null +++ b/doc/pics/akonadi_overview_uml.ps @@ -0,0 +1,2551 @@ +%!PS-Adobe-3.0 +%%BeginProcSet: reencode 1.0 0 +/RE +{ findfont begin + currentdict dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /FontName exch def dup length 0 ne + { /Encoding Encoding 256 array copy def + 0 exch + { dup type /nametype eq + { Encoding 2 index 2 index put + pop 1 add + } + { exch pop + } ifelse + } forall + } if pop + currentdict dup end end + /FontName get exch definefont pop + } bind def +%%EndProcSet: reencode 1.0 0 +%%BeginProcSet: ellipse 1.0 0 +/ellipsedict 8 dict def +ellipsedict /mtrx matrix put +/ellipse { ellipsedict begin +/endangle exch def +/startangle exch def +/yrad exch def +/xrad exch def +/y exch def +/x exch def +/savematrix mtrx currentmatrix def +x y translate +xrad yrad scale +0 0 1 0 360 arc +savematrix setmatrix end } def +%%EndProcSet: ellipse 1.0 0 +%%EndProlog +%%BeginSetup +/isolatin1encoding +[ 32 /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright + /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash /zero /one + /two /three /four /five /six /seven /eight /nine /colon /semicolon + /less /equal /greater /question /at /A /B /C /D /E + /F /G /H /I /J /K /L /M /N /O + /P /Q /R /S /T /U /V /W /X /Y + /Z /bracketleft /backslash /bracketright /asciicircum /underscore /quoteleft /a /b /c + /d /e /f /g /h /i /j /k /l /m + /n /o /p /q /r /s /t /u /v /w + /x /y /z /braceleft /bar /braceright /asciitilde /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef + /space /exclamdown /cent /sterling /currency /yen /brokenbar /section /dieresis /copyright + /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron /degree /plusminus /twosuperior /threesuperior + /acute /mu /paragraph /periodcentered /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf + /threequarters /questiondown /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla + /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis /Eth /Ntilde + /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply /Oslash /Ugrave /Uacute /Ucircumflex + /Udieresis /Yacute /Thorn /germandbls /agrave /aacute /acircumflex /atilde /adieresis /aring + /ae /ccedilla /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis + /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide /oslash /ugrave + /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def +%%EndSetup +1 setlinewidth +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +12 scalefont setfont +0.0 0.0 0.0 setrgbcolor +32 810 translate +0.625 0.625 scale +-20 -12 translate +newpath +20 -12 moveto +855 0 rlineto +0 -1195 rlineto +-855 0 rlineto +closepath +clip +1.0 1.0 1.0 setrgbcolor +newpath +24 -640 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -640 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +28 -653 moveto +(server) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -660 moveto +847 0 rlineto +0 -459 rlineto +-847 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -660 moveto +847 0 rlineto +0 -459 rlineto +-847 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +392 -920 moveto +390 0 rlineto +0 -20 rlineto +-390 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +392 -920 moveto +390 0 rlineto +0 -20 rlineto +-390 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +396 -933 moveto +(storage) show +1.0 1.0 1.0 setrgbcolor +newpath +392 -940 moveto +439 0 rlineto +0 -147 rlineto +-439 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +392 -940 moveto +439 0 rlineto +0 -147 rlineto +-439 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +488 -1032 moveto +95 0 rlineto +0 -45 rlineto +-95 0 rlineto +closepath +eofill +newpath +488 -1032 moveto +95 0 rlineto +0 -1 rlineto +-95 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +488 -1032 moveto +96 0 rlineto +0 -2 rlineto +-96 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +488 -1034 moveto +95 0 rlineto +0 -19 rlineto +-95 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +512 -1047 moveto +(DataStore) show +newpath +488 -1032 moveto +96 0 rlineto +0 -46 rlineto +-96 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +408 -944 moveto +108 0 rlineto +0 -21 rlineto +-108 0 rlineto +closepath +eofill +newpath +408 -944 moveto +108 0 rlineto +0 -1 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +408 -944 moveto +109 0 rlineto +0 -2 rlineto +-109 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +408 -946 moveto +108 0 rlineto +0 -19 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +412 -959 moveto +(NotificationCollector) show +newpath +408 -944 moveto +109 0 rlineto +0 -22 rlineto +-109 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -1048 moveto +67 0 rlineto +0 -21 rlineto +-67 0 rlineto +closepath +eofill +newpath +736 -1048 moveto +67 0 rlineto +0 -1 rlineto +-67 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +736 -1048 moveto +68 0 rlineto +0 -2 rlineto +-68 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -1050 moveto +67 0 rlineto +0 -19 rlineto +-67 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +740 -1063 moveto +(DbInitializer) show +newpath +736 -1048 moveto +68 0 rlineto +0 -22 rlineto +-68 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +520 -1032 moveto +520 -984 lineto +stroke +newpath +520 -984 moveto +517 -966 lineto +stroke +[] 0 setdash +newpath +525 -976 moveto +517 -966 lineto +stroke +newpath +512 -978 moveto +517 -966 lineto +stroke +545 -1005 moveto +(send changes) show +1.0 1.0 1.0 setrgbcolor +newpath +656 -1144 moveto +88 0 rlineto +0 -59 rlineto +-88 0 rlineto +closepath +eofill +newpath +656 -1144 moveto +88 0 rlineto +0 -14 rlineto +-88 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +656 -1144 moveto +89 0 rlineto +0 -15 rlineto +-89 0 rlineto +closepath +stroke +662 -1158 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +656 -1159 moveto +88 0 rlineto +0 -19 rlineto +-88 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +660 -1172 moveto +(MySQL Database) show +1.0 1.0 1.0 setrgbcolor +newpath +657 -1180 moveto +87 0 rlineto +0 -22 rlineto +-87 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +657 -1180 moveto +745 -1180 lineto +stroke +newpath +656 -1144 moveto +89 0 rlineto +0 -60 rlineto +-89 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +737 -1070 moveto +737 -1144 lineto +stroke +[] 0 setdash +newpath +730 -1132 moveto +737 -1144 lineto +stroke +newpath +744 -1132 moveto +737 -1144 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +584 -1078 moveto +656 -1144 lineto +stroke +[] 0 setdash +newpath +642 -1141 moveto +656 -1144 lineto +stroke +newpath +651 -1130 moveto +656 -1144 lineto +stroke +0.78431374 1.0 1.0 setrgbcolor +newpath +40 -864 moveto +94 0 rlineto +0 -21 rlineto +-94 0 rlineto +closepath +eofill +newpath +40 -864 moveto +94 0 rlineto +0 -1 rlineto +-94 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +40 -864 moveto +95 0 rlineto +0 -2 rlineto +-95 0 rlineto +closepath +stroke +0.78431374 1.0 1.0 setrgbcolor +newpath +40 -866 moveto +94 0 rlineto +0 -19 rlineto +-94 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +44 -879 moveto +(RecourceManager) show +newpath +40 -864 moveto +95 0 rlineto +0 -22 rlineto +-95 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +584 -1056 moveto +736 -1056 lineto +stroke +[] 0 setdash +newpath +724 -1063 moveto +736 -1056 lineto +stroke +newpath +724 -1049 moveto +736 -1056 lineto +stroke +615 -1037 moveto +(build db) show +1.0 1.0 1.0 setrgbcolor +newpath +696 -720 moveto +102 0 rlineto +0 -20 rlineto +-102 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +696 -720 moveto +102 0 rlineto +0 -20 rlineto +-102 0 rlineto +closepath +stroke +700 -733 moveto +(handler) show +1.0 1.0 1.0 setrgbcolor +newpath +696 -740 moveto +151 0 rlineto +0 -163 rlineto +-151 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +696 -740 moveto +151 0 rlineto +0 -163 rlineto +-151 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +704 -744 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +704 -744 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +704 -744 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +704 -746 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +715 -759 moveto +(Handler) show +newpath +704 -744 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -872 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +736 -872 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +736 -872 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +736 -874 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +751 -887 moveto +(Status) show +newpath +736 -872 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +newpath +744 -872 moveto +744 -766 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +744 -766 moveto +751 -778 lineto +737 -778 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +744 -766 moveto +751 -778 lineto +737 -778 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +784 -800 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +784 -800 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +784 -800 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +784 -802 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +795 -815 moveto +(Append) show +newpath +784 -800 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +newpath +784 -800 moveto +764 -766 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +764 -766 moveto +776 -772 lineto +764 -779 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +764 -766 moveto +776 -772 lineto +764 -779 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +768 -840 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +768 -840 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +768 -840 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +768 -842 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +782 -855 moveto +(Delete) show +newpath +768 -840 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +newpath +768 -840 moveto +764 -766 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +764 -766 moveto +771 -777 lineto +757 -778 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +764 -766 moveto +771 -777 lineto +757 -778 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +536 -704 moveto +102 0 rlineto +0 -36 rlineto +-102 0 rlineto +closepath +eofill +newpath +536 -704 moveto +102 0 rlineto +0 -14 rlineto +-102 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +536 -704 moveto +103 0 rlineto +0 -15 rlineto +-103 0 rlineto +closepath +stroke +555 -718 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +536 -719 moveto +102 0 rlineto +0 -19 rlineto +-102 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +540 -732 moveto +(AkonadiConnection) show +newpath +536 -704 moveto +103 0 rlineto +0 -37 rlineto +-103 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +568 -840 moveto +75 0 rlineto +0 -36 rlineto +-75 0 rlineto +closepath +eofill +newpath +568 -840 moveto +75 0 rlineto +0 -14 rlineto +-75 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +568 -840 moveto +76 0 rlineto +0 -15 rlineto +-76 0 rlineto +closepath +stroke +574 -854 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +568 -855 moveto +75 0 rlineto +0 -19 rlineto +-75 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +572 -868 moveto +(CacheCleaner) show +newpath +568 -840 moveto +76 0 rlineto +0 -37 rlineto +-76 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +639 -728 moveto +696 -728 lineto +stroke +[] 0 setdash +newpath +684 -735 moveto +696 -728 lineto +stroke +newpath +684 -721 moveto +696 -728 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +368 -776 moveto +183 0 rlineto +0 -34 rlineto +-183 0 rlineto +closepath +eofill +newpath +368 -776 moveto +183 0 rlineto +0 -14 rlineto +-183 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +368 -776 moveto +184 0 rlineto +0 -15 rlineto +-184 0 rlineto +closepath +stroke +420 -790 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +368 -791 moveto +183 0 rlineto +0 -19 rlineto +-183 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +425 -804 moveto +(AkonadiServer) show +newpath +368 -776 moveto +184 0 rlineto +0 -35 rlineto +-184 0 rlineto +closepath +stroke +newpath +368 -811 moveto +135 -864 lineto +stroke +newpath +368 -811 moveto +359 -818 lineto +349 -815 lineto +357 -808 lineto +closepath +eofill +newpath +368 -811 moveto +359 -818 lineto +349 -815 lineto +357 -808 lineto +closepath +stroke +353 -833 moveto +(1) show +149 -879 moveto +(1) show +newpath +552 -811 moveto +568 -840 lineto +stroke +newpath +552 -811 moveto +561 -817 lineto +561 -828 lineto +552 -822 lineto +closepath +eofill +newpath +552 -811 moveto +561 -817 lineto +561 -828 lineto +552 -822 lineto +closepath +stroke +569 -822 moveto +(1) show +570 -824 moveto +(1) show +newpath +536 -811 moveto +536 -1032 lineto +stroke +newpath +536 -811 moveto +541 -821 lineto +536 -831 lineto +531 -821 lineto +closepath +eofill +newpath +536 -811 moveto +541 -821 lineto +536 -831 lineto +531 -821 lineto +closepath +stroke +548 -831 moveto +(1) show +548 -1022 moveto +(1) show +1.0 1.0 0.78431374 setrgbcolor +newpath +208 -760 moveto +107 0 rlineto +0 -21 rlineto +-107 0 rlineto +closepath +eofill +newpath +208 -760 moveto +107 0 rlineto +0 -1 rlineto +-107 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +208 -760 moveto +108 0 rlineto +0 -2 rlineto +-108 0 rlineto +closepath +stroke +1.0 1.0 0.78431374 setrgbcolor +newpath +208 -762 moveto +107 0 rlineto +0 -19 rlineto +-107 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +212 -775 moveto +(NotificationManager) show +newpath +208 -760 moveto +108 0 rlineto +0 -22 rlineto +-108 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +336 -712 moveto +108 0 rlineto +0 -21 rlineto +-108 0 rlineto +closepath +eofill +newpath +336 -712 moveto +108 0 rlineto +0 -1 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +336 -712 moveto +109 0 rlineto +0 -2 rlineto +-109 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +336 -714 moveto +108 0 rlineto +0 -19 rlineto +-108 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +340 -727 moveto +(CachePolicyManager) show +newpath +336 -712 moveto +109 0 rlineto +0 -22 rlineto +-109 0 rlineto +closepath +stroke +newpath +376 -776 moveto +376 -734 lineto +stroke +newpath +376 -776 moveto +371 -766 lineto +376 -756 lineto +381 -766 lineto +closepath +eofill +newpath +376 -776 moveto +371 -766 lineto +376 -756 lineto +381 -766 lineto +closepath +stroke +358 -766 moveto +(1) show +358 -754 moveto +(1) show +newpath +368 -782 moveto +316 -782 lineto +stroke +newpath +368 -782 moveto +358 -787 lineto +348 -782 lineto +358 -777 lineto +closepath +eofill +newpath +368 -782 moveto +358 -787 lineto +348 -782 lineto +358 -777 lineto +closepath +stroke +350 -802 moveto +(1) show +328 -802 moveto +(1) show +[5.0 5.0 ] 0 setdash +newpath +135 -886 moveto +488 -1032 lineto +stroke +[] 0 setdash +newpath +474 -1033 moveto +488 -1032 lineto +stroke +newpath +479 -1020 moveto +488 -1032 lineto +stroke +285 -942 moveto +(keep DS up to date) show +[5.0 5.0 ] 0 setdash +newpath +544 -776 moveto +544 -741 lineto +stroke +[] 0 setdash +newpath +551 -753 moveto +544 -741 lineto +stroke +newpath +537 -753 moveto +544 -741 lineto +stroke +575 -765 moveto +(use) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -496 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -496 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +stroke +28 -509 moveto +(server/control) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -516 moveto +847 0 rlineto +0 -91 rlineto +-847 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -516 moveto +847 0 rlineto +0 -91 rlineto +-847 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +0.78431374 1.0 1.0 setrgbcolor +newpath +104 -576 moveto +79 0 rlineto +0 -21 rlineto +-79 0 rlineto +closepath +eofill +newpath +104 -576 moveto +79 0 rlineto +0 -1 rlineto +-79 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +104 -576 moveto +80 0 rlineto +0 -2 rlineto +-80 0 rlineto +closepath +stroke +0.78431374 1.0 1.0 setrgbcolor +newpath +104 -578 moveto +79 0 rlineto +0 -19 rlineto +-79 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +108 -591 moveto +(AgentManager) show +newpath +104 -576 moveto +80 0 rlineto +0 -22 rlineto +-80 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +304 -576 moveto +82 0 rlineto +0 -21 rlineto +-82 0 rlineto +closepath +eofill +newpath +304 -576 moveto +82 0 rlineto +0 -1 rlineto +-82 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +304 -576 moveto +83 0 rlineto +0 -2 rlineto +-83 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +304 -578 moveto +82 0 rlineto +0 -19 rlineto +-82 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +308 -591 moveto +(ProfileManager) show +newpath +304 -576 moveto +83 0 rlineto +0 -22 rlineto +-83 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +496 -576 moveto +122 0 rlineto +0 -21 rlineto +-122 0 rlineto +closepath +eofill +newpath +496 -576 moveto +122 0 rlineto +0 -1 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +496 -576 moveto +123 0 rlineto +0 -2 rlineto +-123 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +496 -578 moveto +122 0 rlineto +0 -19 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +500 -591 moveto +(SearchProviderManager) show +newpath +496 -576 moveto +123 0 rlineto +0 -22 rlineto +-123 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +632 -640 moveto +81 0 rlineto +0 -40 rlineto +-81 0 rlineto +closepath +eofill +newpath +632 -640 moveto +81 0 rlineto +0 -14 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +632 -640 moveto +82 0 rlineto +0 -15 rlineto +-82 0 rlineto +closepath +stroke +635 -654 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +632 -655 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +660 -668 moveto +(IMAP) show +newpath +632 -640 moveto +82 0 rlineto +0 -41 rlineto +-82 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +632 -704 moveto +632 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +632 -681 moveto +639 -693 lineto +625 -693 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +632 -681 moveto +639 -693 lineto +625 -693 lineto +closepath +stroke +669 -706 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +184 -640 moveto +81 0 rlineto +0 -40 rlineto +-81 0 rlineto +closepath +eofill +newpath +184 -640 moveto +81 0 rlineto +0 -14 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +184 -640 moveto +82 0 rlineto +0 -15 rlineto +-82 0 rlineto +closepath +stroke +187 -654 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +184 -655 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +213 -668 moveto +(DBus) show +newpath +184 -640 moveto +82 0 rlineto +0 -41 rlineto +-82 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +258 -760 moveto +258 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +258 -681 moveto +265 -693 lineto +251 -693 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +258 -681 moveto +265 -693 lineto +251 -693 lineto +closepath +stroke +295 -734 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +135 -864 moveto +184 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +184 -681 moveto +187 -694 lineto +174 -690 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +184 -681 moveto +187 -694 lineto +174 -690 lineto +closepath +stroke +194 -781 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +336 -712 moveto +266 -681 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +266 -681 moveto +279 -679 lineto +274 -692 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +266 -681 moveto +279 -679 lineto +274 -692 lineto +closepath +stroke +296 -688 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +184 -598 moveto +184 -640 lineto +stroke +[] 0 setdash +newpath +177 -628 moveto +184 -640 lineto +stroke +newpath +191 -628 moveto +184 -640 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +336 -496 moveto +81 0 rlineto +0 -40 rlineto +-81 0 rlineto +closepath +eofill +newpath +336 -496 moveto +81 0 rlineto +0 -14 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +336 -496 moveto +82 0 rlineto +0 -15 rlineto +-82 0 rlineto +closepath +stroke +339 -510 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +336 -511 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +365 -524 moveto +(DBus) show +newpath +336 -496 moveto +82 0 rlineto +0 -41 rlineto +-82 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +184 -576 moveto +336 -537 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +336 -537 moveto +326 -546 lineto +322 -533 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +336 -537 moveto +326 -546 lineto +322 -533 lineto +closepath +stroke +241 -546 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +377 -576 moveto +377 -537 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +377 -537 moveto +384 -549 lineto +370 -549 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +377 -537 moveto +384 -549 lineto +370 -549 lineto +closepath +stroke +414 -570 moveto +(<>) show +[5.0 5.0 ] 0 setdash +newpath +496 -576 moveto +418 -537 lineto +stroke +1.0 1.0 1.0 setrgbcolor +[] 0 setdash +newpath +418 -537 moveto +431 -536 lineto +425 -548 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +418 -537 moveto +431 -536 lineto +425 -548 lineto +closepath +stroke +453 -548 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +32 -384 moveto +158 0 rlineto +0 -20 rlineto +-158 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +32 -384 moveto +158 0 rlineto +0 -20 rlineto +-158 0 rlineto +closepath +stroke +36 -397 moveto +(resources) show +1.0 1.0 1.0 setrgbcolor +newpath +32 -404 moveto +207 0 rlineto +0 -71 rlineto +-207 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +32 -404 moveto +207 0 rlineto +0 -71 rlineto +-207 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +296 -384 moveto +190 0 rlineto +0 -20 rlineto +-190 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +296 -384 moveto +190 0 rlineto +0 -20 rlineto +-190 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +300 -397 moveto +(searchprovider) show +1.0 1.0 1.0 setrgbcolor +newpath +296 -404 moveto +239 0 rlineto +0 -75 rlineto +-239 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +296 -404 moveto +239 0 rlineto +0 -75 rlineto +-239 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +24 -128 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -128 moveto +798 0 rlineto +0 -20 rlineto +-798 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +28 -141 moveto +(libakonadi) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -148 moveto +847 0 rlineto +0 -203 rlineto +-847 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -148 moveto +847 0 rlineto +0 -203 rlineto +-847 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +48 -440 moveto +81 0 rlineto +0 -21 rlineto +-81 0 rlineto +closepath +eofill +newpath +48 -440 moveto +81 0 rlineto +0 -1 rlineto +-81 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +48 -440 moveto +82 0 rlineto +0 -2 rlineto +-82 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +48 -442 moveto +81 0 rlineto +0 -19 rlineto +-81 0 rlineto +closepath +eofill +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +0.0 0.0 0.0 setrgbcolor +52 -455 moveto +(VCardResource) show +newpath +48 -440 moveto +82 0 rlineto +0 -22 rlineto +-82 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +152 -440 moveto +70 0 rlineto +0 -21 rlineto +-70 0 rlineto +closepath +eofill +newpath +152 -440 moveto +70 0 rlineto +0 -1 rlineto +-70 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +152 -440 moveto +71 0 rlineto +0 -2 rlineto +-71 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +152 -442 moveto +70 0 rlineto +0 -19 rlineto +-70 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +156 -455 moveto +(ICalResource) show +newpath +152 -440 moveto +71 0 rlineto +0 -22 rlineto +-71 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +96 -288 moveto +99 0 rlineto +0 -36 rlineto +-99 0 rlineto +closepath +eofill +newpath +96 -288 moveto +99 0 rlineto +0 -14 rlineto +-99 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +96 -288 moveto +100 0 rlineto +0 -15 rlineto +-100 0 rlineto +closepath +stroke +99 -302 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +96 -303 moveto +99 0 rlineto +0 -19 rlineto +-99 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +112 -316 moveto +(ResourceBase) show +newpath +96 -288 moveto +100 0 rlineto +0 -37 rlineto +-100 0 rlineto +closepath +stroke +1.0 1.0 0.78431374 setrgbcolor +newpath +248 -288 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +248 -288 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +248 -288 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 0.78431374 setrgbcolor +newpath +248 -290 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +259 -303 moveto +(Monitor) show +newpath +248 -288 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +432 -280 moveto +123 0 rlineto +0 -36 rlineto +-123 0 rlineto +closepath +eofill +newpath +432 -280 moveto +123 0 rlineto +0 -14 rlineto +-123 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +432 -280 moveto +124 0 rlineto +0 -15 rlineto +-124 0 rlineto +closepath +stroke +435 -294 moveto +(<>) show +1.0 1.0 1.0 setrgbcolor +newpath +432 -295 moveto +123 0 rlineto +0 -19 rlineto +-123 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +446 -308 moveto +(SearchProviderBase) show +newpath +432 -280 moveto +124 0 rlineto +0 -37 rlineto +-124 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +256 -310 moveto +256 -640 lineto +stroke +[] 0 setdash +newpath +249 -628 moveto +256 -640 lineto +stroke +newpath +263 -628 moveto +256 -640 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +144 -576 moveto +144 -325 lineto +stroke +[] 0 setdash +newpath +151 -337 moveto +144 -325 lineto +stroke +newpath +137 -337 moveto +144 -325 lineto +stroke +183 -427 moveto +(create) show +newpath +114 -440 moveto +114 -325 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +114 -325 moveto +121 -337 lineto +107 -337 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +114 -325 moveto +121 -337 lineto +107 -337 lineto +closepath +stroke +newpath +168 -440 moveto +168 -325 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +168 -325 moveto +175 -337 lineto +161 -337 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +168 -325 moveto +175 -337 lineto +161 -337 lineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +548 -576 moveto +548 -317 lineto +stroke +[] 0 setdash +newpath +555 -329 moveto +548 -317 lineto +stroke +newpath +541 -329 moveto +548 -317 lineto +stroke +573 -453 moveto +(create) show +1.0 1.0 1.0 setrgbcolor +newpath +312 -448 moveto +122 0 rlineto +0 -21 rlineto +-122 0 rlineto +closepath +eofill +newpath +312 -448 moveto +122 0 rlineto +0 -1 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +312 -448 moveto +123 0 rlineto +0 -2 rlineto +-123 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +312 -450 moveto +122 0 rlineto +0 -19 rlineto +-122 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +316 -463 moveto +(MessageSearchProvider) show +newpath +312 -448 moveto +123 0 rlineto +0 -22 rlineto +-123 0 rlineto +closepath +stroke +newpath +432 -448 moveto +432 -317 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +432 -317 moveto +439 -329 lineto +425 -329 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +432 -317 moveto +439 -329 lineto +425 -329 lineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +576 -877 moveto +576 -1032 lineto +stroke +[] 0 setdash +newpath +569 -1020 moveto +576 -1032 lineto +stroke +newpath +583 -1020 moveto +576 -1032 lineto +stroke +601 -960 moveto +(clear old values) show +1.0 1.0 1.0 setrgbcolor +newpath +624 -176 moveto +59 0 rlineto +0 -21 rlineto +-59 0 rlineto +closepath +eofill +newpath +624 -176 moveto +59 0 rlineto +0 -1 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +624 -176 moveto +60 0 rlineto +0 -2 rlineto +-60 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +624 -178 moveto +59 0 rlineto +0 -19 rlineto +-59 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +646 -191 moveto +(Job) show +newpath +624 -176 moveto +60 0 rlineto +0 -22 rlineto +-60 0 rlineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +308 -288 moveto +624 -198 lineto +stroke +[] 0 setdash +newpath +614 -208 moveto +624 -198 lineto +stroke +newpath +610 -194 moveto +624 -198 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +656 -198 moveto +656 -640 lineto +stroke +[] 0 setdash +newpath +649 -628 moveto +656 -640 lineto +stroke +newpath +663 -628 moveto +656 -640 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -240 moveto +76 0 rlineto +0 -21 rlineto +-76 0 rlineto +closepath +eofill +newpath +752 -240 moveto +76 0 rlineto +0 -1 rlineto +-76 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +752 -240 moveto +77 0 rlineto +0 -2 rlineto +-77 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -242 moveto +76 0 rlineto +0 -19 rlineto +-76 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +756 -255 moveto +(ItemDeleteJob) show +newpath +752 -240 moveto +77 0 rlineto +0 -22 rlineto +-77 0 rlineto +closepath +stroke +newpath +752 -240 moveto +684 -198 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +684 -198 moveto +697 -198 lineto +690 -210 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +684 -198 moveto +697 -198 lineto +690 -210 lineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -272 moveto +71 0 rlineto +0 -21 rlineto +-71 0 rlineto +closepath +eofill +newpath +752 -272 moveto +71 0 rlineto +0 -1 rlineto +-71 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +752 -272 moveto +72 0 rlineto +0 -2 rlineto +-72 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +752 -274 moveto +71 0 rlineto +0 -19 rlineto +-71 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +783 -287 moveto +(...) show +newpath +752 -272 moveto +72 0 rlineto +0 -22 rlineto +-72 0 rlineto +closepath +stroke +newpath +752 -272 moveto +684 -198 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +684 -198 moveto +697 -202 lineto +686 -211 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +684 -198 moveto +697 -202 lineto +686 -211 lineto +closepath +stroke +[5.0 5.0 ] 0 setdash +newpath +316 -782 moveto +408 -944 lineto +stroke +[] 0 setdash +newpath +395 -937 moveto +408 -944 lineto +stroke +newpath +408 -930 moveto +408 -944 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +24 -16 moveto +198 0 rlineto +0 -20 rlineto +-198 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -16 moveto +198 0 rlineto +0 -20 rlineto +-198 0 rlineto +closepath +stroke +28 -29 moveto +(kabc) show +1.0 1.0 1.0 setrgbcolor +newpath +24 -36 moveto +247 0 rlineto +0 -79 rlineto +-247 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +24 -36 moveto +247 0 rlineto +0 -79 rlineto +-247 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +304 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +304 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +308 -29 moveto +(kmime) show +1.0 1.0 1.0 setrgbcolor +newpath +304 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +304 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +480 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +480 -16 moveto +90 0 rlineto +0 -20 rlineto +-90 0 rlineto +closepath +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +484 -29 moveto +(kioslave) show +1.0 1.0 1.0 setrgbcolor +newpath +480 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +480 -36 moveto +139 0 rlineto +0 -79 rlineto +-139 0 rlineto +closepath +stroke +isolatin1encoding /_TimesRoman /TimesRoman RE +/_TimesRoman findfont +10 scalefont setfont +1.0 1.0 1.0 setrgbcolor +newpath +40 -48 moveto +239 -48 lineto +249 -58 lineto +249 -105 lineto +40 -105 lineto +40 -48 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +40 -48 moveto +239 -48 lineto +249 -58 lineto +249 -105 lineto +40 -105 lineto +40 -48 lineto +stroke +0.69803923 0.69803923 0.69803923 setrgbcolor +newpath +239 -48 moveto +249 -58 lineto +239 -58 lineto +239 -48 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +239 -48 moveto +249 -58 lineto +239 -58 lineto +239 -48 lineto +stroke +isolatin1encoding /_Helvetica /Helvetica RE +/_Helvetica findfont +10 scalefont setfont +42 -63 moveto +(some widgets to manipulate addressees) show +42 -76 moveto +(using Jobs and Monitor) show +[5.0 5.0 ] 0 setdash +newpath +126 -48 moveto +126 -16 lineto +stroke +newpath +264 -116 moveto +264 -288 lineto +stroke +[] 0 setdash +newpath +257 -276 moveto +264 -288 lineto +stroke +newpath +271 -276 moveto +264 -288 lineto +stroke +[5.0 5.0 ] 0 setdash +newpath +272 -116 moveto +624 -176 lineto +stroke +[] 0 setdash +newpath +610 -180 moveto +624 -176 lineto +stroke +newpath +613 -167 moveto +624 -176 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +328 -64 moveto +80 0 rlineto +0 -21 rlineto +-80 0 rlineto +closepath +eofill +newpath +328 -64 moveto +80 0 rlineto +0 -1 rlineto +-80 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +328 -64 moveto +81 0 rlineto +0 -2 rlineto +-81 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +328 -66 moveto +80 0 rlineto +0 -19 rlineto +-80 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +332 -79 moveto +(MessageModel) show +newpath +328 -64 moveto +81 0 rlineto +0 -22 rlineto +-81 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +512 -64 moveto +72 0 rlineto +0 -21 rlineto +-72 0 rlineto +closepath +eofill +newpath +512 -64 moveto +72 0 rlineto +0 -1 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +512 -64 moveto +73 0 rlineto +0 -2 rlineto +-73 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +512 -66 moveto +72 0 rlineto +0 -19 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +516 -79 moveto +(AkonadiSlave) show +newpath +512 -64 moveto +73 0 rlineto +0 -22 rlineto +-73 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +448 -440 moveto +72 0 rlineto +0 -21 rlineto +-72 0 rlineto +closepath +eofill +newpath +448 -440 moveto +72 0 rlineto +0 -1 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +448 -440 moveto +73 0 rlineto +0 -2 rlineto +-73 0 rlineto +closepath +stroke +1.0 1.0 1.0 setrgbcolor +newpath +448 -442 moveto +72 0 rlineto +0 -19 rlineto +-72 0 rlineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +452 -455 moveto +(StrigiProvider) show +newpath +448 -440 moveto +73 0 rlineto +0 -22 rlineto +-73 0 rlineto +closepath +stroke +newpath +494 -440 moveto +494 -317 lineto +stroke +1.0 1.0 1.0 setrgbcolor +newpath +494 -317 moveto +501 -329 lineto +487 -329 lineto +closepath +eofill +0.0 0.0 0.0 setrgbcolor +newpath +494 -317 moveto +501 -329 lineto +487 -329 lineto +closepath +stroke +showpage +%%Trailer diff --git a/doc/pics/akonadi_overview_uml_small.png b/doc/pics/akonadi_overview_uml_small.png new file mode 100644 index 00000000..06112b1d Binary files /dev/null and b/doc/pics/akonadi_overview_uml_small.png differ diff --git a/doc/pics/concept.eps b/doc/pics/concept.eps new file mode 100644 index 00000000..f2a89131 --- /dev/null +++ b/doc/pics/concept.eps @@ -0,0 +1,2351 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%For: +%%Title: +%%Creator: Scribus1.3.4 +%%Pages: 1 +%%BoundingBox: 0 0 595 420 +%%HiResBoundingBox: 0 0 595.28 419.53 +%%LanguageLevel: 3 +%%EndComments +%%BeginProlog +/Scribusdict 100 dict def +Scribusdict begin +/sp {showpage} bind def +/oldsetgray /setgray load def +/cmyk {setcmykcolor} def +/m {moveto} bind def +/l {lineto} bind def +/li {lineto} bind def +/cu {curveto} bind def +/cl {closepath} bind def +/gs {gsave} bind def +/gr {grestore} bind def +/tr {translate} bind def +/ro {rotate} bind def +/sh {show} bind def +/shg {setcmykcolor moveto glyphshow} def +/shgsp {moveto glyphshow} def +/sc {scale} bind def +/se {selectfont} bind def +/sf {setfont} bind def +/sw {setlinewidth} bind def +/f {findfont} bind def +/fi {fill} bind def +/st {stroke} bind def +/shgf {gs dup scale begin cvx exec fill end gr} bind def +/shgs {gs dup 1 exch div currentlinewidth mul sw dup scale + begin cvx exec st end gr} bind def +/bEPS { + /b4_Inc_state save def + /dict_count countdictstack def + /op_count count 1 sub def + userdict begin + /showpage { } def + 0 setgray 0 setlinecap + 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [ ] 0 setdash newpath + /languagelevel where + {pop languagelevel + 1 ne + {false setstrokeadjust false setoverprint + } if } if } bind def +/eEPS { count op_count sub {pop} repeat + countdictstack dict_count sub {end} repeat + b4_Inc_state restore } bind def + end +%%EndProlog +%%BeginSetup +/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse +/ArialMT 37 dict def +ArialMT begin +/G11 { newpath +2.33891 -12.1045 m +2.33891 -12.1045 1.61141 -11.1866 1.10844 -9.95609 cu +1.10844 -9.95609 0.605469 -8.72563 0.605469 -7.40719 cu +0.605469 -7.40719 0.605469 -6.24516 0.981406 -5.18063 cu +0.981406 -5.18063 1.42094 -3.94531 2.33891 -2.71969 cu +2.33891 -2.71969 2.96875 -2.71969 2.96875 -2.71969 cu +2.96875 -2.71969 2.37797 -3.73531 2.1875 -4.16984 cu +2.1875 -4.16984 1.88969 -4.84375 1.71875 -5.57609 cu +1.71875 -5.57609 1.50875 -6.48922 1.50875 -7.41203 cu +1.50875 -7.41203 1.50875 -9.76078 2.96875 -12.1045 cu +2.96875 -12.1045 2.33891 -12.1045 2.33891 -12.1045 cu +cl +} bind def +/G12 { newpath +1.23531 -12.1045 m +1.23531 -12.1045 0.605469 -12.1045 0.605469 -12.1045 cu +0.605469 -12.1045 2.06547 -9.76078 2.06547 -7.41203 cu +2.06547 -7.41203 2.06547 -6.49406 1.85547 -5.59078 cu +1.85547 -5.59078 1.68953 -4.85844 1.39156 -4.18453 cu +1.39156 -4.18453 1.20125 -3.74516 0.605469 -2.71969 cu +0.605469 -2.71969 1.23531 -2.71969 1.23531 -2.71969 cu +1.23531 -2.71969 2.15328 -3.94531 2.59281 -5.18063 cu +2.59281 -5.18063 2.96875 -6.24516 2.96875 -7.40719 cu +2.96875 -7.40719 2.96875 -8.72563 2.46328 -9.95609 cu +2.46328 -9.95609 1.95797 -11.1866 1.23531 -12.1045 cu +cl +} bind def +/G16 { newpath +0.317344 -7.85156 m +0.317344 -7.85156 0.317344 -6.96781 0.317344 -6.96781 cu +0.317344 -6.96781 3.01766 -6.96781 3.01766 -6.96781 cu +3.01766 -6.96781 3.01766 -7.85156 3.01766 -7.85156 cu +3.01766 -7.85156 0.317344 -7.85156 0.317344 -7.85156 cu +cl +} bind def +/G18 { newpath +0 -10.122 m +0 -10.122 2.07516 -2.71969 2.07516 -2.71969 cu +2.07516 -2.71969 2.77828 -2.71969 2.77828 -2.71969 cu +2.77828 -2.71969 0.707969 -10.122 0.707969 -10.122 cu +0.707969 -10.122 0 -10.122 0 -10.122 cu +cl +} bind def +/G20 { newpath +3.72562 -10 m +3.72562 -10 2.84672 -10 2.84672 -10 cu +2.84672 -10 2.84672 -4.39938 2.84672 -4.39938 cu +2.84672 -4.39938 2.52937 -4.70219 2.01422 -5.005 cu +2.01422 -5.005 1.49906 -5.30766 1.08891 -5.45891 cu +1.08891 -5.45891 1.08891 -4.60938 1.08891 -4.60938 cu +1.08891 -4.60938 1.82625 -4.26266 2.37797 -3.76953 cu +2.37797 -3.76953 2.92969 -3.27641 3.15922 -2.8125 cu +3.15922 -2.8125 3.72562 -2.8125 3.72562 -2.8125 cu +3.72562 -2.8125 3.72562 -10 3.72562 -10 cu +cl +} bind def +/G21 { newpath +5.03422 -9.15531 m +5.03422 -9.15531 5.03422 -10 5.03422 -10 cu +5.03422 -10 0.302812 -10 0.302812 -10 cu +0.302812 -10 0.292969 -9.68266 0.405313 -9.38969 cu +0.405313 -9.38969 0.585938 -8.90625 0.983906 -8.4375 cu +0.983906 -8.4375 1.38187 -7.96875 2.13375 -7.35344 cu +2.13375 -7.35344 3.30078 -6.39641 3.71094 -5.83734 cu +3.71094 -5.83734 4.12109 -5.27828 4.12109 -4.78031 cu +4.12109 -4.78031 4.12109 -4.25781 3.7475 -3.89891 cu +3.7475 -3.89891 3.37406 -3.54 2.77344 -3.54 cu +2.77344 -3.54 2.13875 -3.54 1.75781 -3.92094 cu +1.75781 -3.92094 1.37703 -4.30172 1.37203 -4.97562 cu +1.37203 -4.97562 0.46875 -4.88281 0.46875 -4.88281 cu +0.46875 -4.88281 0.561562 -3.87203 1.16703 -3.34234 cu +1.16703 -3.34234 1.7725 -2.8125 2.79297 -2.8125 cu +2.79297 -2.8125 3.82328 -2.8125 4.42375 -3.38375 cu +4.42375 -3.38375 5.02438 -3.955 5.02438 -4.79984 cu +5.02438 -4.79984 5.02438 -5.22953 4.84859 -5.64453 cu +4.84859 -5.64453 4.67281 -6.05953 4.26516 -6.51859 cu +4.26516 -6.51859 3.8575 -6.9775 2.91016 -7.77828 cu +2.91016 -7.77828 2.11922 -8.44234 1.89453 -8.67922 cu +1.89453 -8.67922 1.67 -8.91594 1.52344 -9.15531 cu +1.52344 -9.15531 5.03422 -9.15531 5.03422 -9.15531 cu +cl +} bind def +/G36 { newpath +-0.0146875 -10 m +-0.0146875 -10 2.73438 -2.84172 2.73438 -2.84172 cu +2.73438 -2.84172 3.75484 -2.84172 3.75484 -2.84172 cu +3.75484 -2.84172 6.68453 -10 6.68453 -10 cu +6.68453 -10 5.60547 -10 5.60547 -10 cu +5.60547 -10 4.77047 -7.83203 4.77047 -7.83203 cu +4.77047 -7.83203 1.77734 -7.83203 1.77734 -7.83203 cu +1.77734 -7.83203 0.99125 -10 0.99125 -10 cu +0.99125 -10 -0.0146875 -10 -0.0146875 -10 cu +-0.0146875 -10 -0.0146875 -10 -0.0146875 -10 cu +cl +2.05078 -7.06047 m +2.05078 -7.06047 4.4775 -7.06047 4.4775 -7.06047 cu +4.4775 -7.06047 3.73047 -5.07812 3.73047 -5.07812 cu +3.73047 -5.07812 3.38875 -4.17484 3.22266 -3.59375 cu +3.22266 -3.59375 3.08594 -4.28219 2.83688 -4.96094 cu +2.83688 -4.96094 2.05078 -7.06047 2.05078 -7.06047 cu +cl +} bind def +/G37 { newpath +0.7325 -10 m +0.7325 -10 0.7325 -2.84172 0.7325 -2.84172 cu +0.7325 -2.84172 3.41797 -2.84172 3.41797 -2.84172 cu +3.41797 -2.84172 4.23828 -2.84172 4.73391 -3.05906 cu +4.73391 -3.05906 5.22953 -3.27641 5.51031 -3.72812 cu +5.51031 -3.72812 5.79109 -4.17969 5.79109 -4.67281 cu +5.79109 -4.67281 5.79109 -5.13187 5.54203 -5.53719 cu +5.54203 -5.53719 5.29297 -5.94234 4.79 -6.19141 cu +4.79 -6.19141 5.43953 -6.38187 5.78859 -6.84094 cu +5.78859 -6.84094 6.13766 -7.29984 6.13766 -7.92484 cu +6.13766 -7.92484 6.13766 -8.42766 5.92531 -8.85984 cu +5.92531 -8.85984 5.71297 -9.29203 5.40047 -9.52641 cu +5.40047 -9.52641 5.08797 -9.76078 4.61672 -9.88047 cu +4.61672 -9.88047 4.14547 -10 3.46188 -10 cu +3.46188 -10 0.7325 -10 0.7325 -10 cu +0.7325 -10 0.7325 -10 0.7325 -10 cu +cl +1.67969 -5.84953 m +1.67969 -5.84953 3.2275 -5.84953 3.2275 -5.84953 cu +3.2275 -5.84953 3.8575 -5.84953 4.13094 -5.76656 cu +4.13094 -5.76656 4.49219 -5.65922 4.67531 -5.41016 cu +4.67531 -5.41016 4.85844 -5.16109 4.85844 -4.78516 cu +4.85844 -4.78516 4.85844 -4.42875 4.6875 -4.15781 cu +4.6875 -4.15781 4.51656 -3.88672 4.19922 -3.78672 cu +4.19922 -3.78672 3.88187 -3.68656 3.11031 -3.68656 cu +3.11031 -3.68656 1.67969 -3.68656 1.67969 -3.68656 cu +1.67969 -3.68656 1.67969 -5.84953 1.67969 -5.84953 cu +1.67969 -5.84953 1.67969 -5.84953 1.67969 -5.84953 cu +cl +1.67969 -9.15531 m +1.67969 -9.15531 3.46188 -9.15531 3.46188 -9.15531 cu +3.46188 -9.15531 3.92094 -9.15531 4.10641 -9.12109 cu +4.10641 -9.12109 4.43359 -9.0625 4.65328 -8.92578 cu +4.65328 -8.92578 4.87312 -8.78906 5.01469 -8.52781 cu +5.01469 -8.52781 5.15625 -8.26656 5.15625 -7.92484 cu +5.15625 -7.92484 5.15625 -7.52438 4.95109 -7.22906 cu +4.95109 -7.22906 4.74609 -6.93359 4.38234 -6.81406 cu +4.38234 -6.81406 4.01859 -6.69437 3.335 -6.69437 cu +3.335 -6.69437 1.67969 -6.69437 1.67969 -6.69437 cu +1.67969 -6.69437 1.67969 -9.15531 1.67969 -9.15531 cu +cl +} bind def +/G38 { newpath +5.87891 -7.49016 m +5.87891 -7.49016 6.82625 -7.72953 6.82625 -7.72953 cu +6.82625 -7.72953 6.52828 -8.89641 5.75438 -9.50922 cu +5.75438 -9.50922 4.98047 -10.122 3.86234 -10.122 cu +3.86234 -10.122 2.70516 -10.122 1.98 -9.65094 cu +1.98 -9.65094 1.25484 -9.17969 0.876406 -8.28609 cu +0.876406 -8.28609 0.498125 -7.3925 0.498125 -6.36719 cu +0.498125 -6.36719 0.498125 -5.24906 0.925312 -4.41656 cu +0.925312 -4.41656 1.3525 -3.58391 2.14109 -3.15187 cu +2.14109 -3.15187 2.92969 -2.71969 3.87703 -2.71969 cu +3.87703 -2.71969 4.95125 -2.71969 5.68359 -3.26656 cu +5.68359 -3.26656 6.41609 -3.81344 6.70406 -4.80469 cu +6.70406 -4.80469 5.77156 -5.02438 5.77156 -5.02438 cu +5.77156 -5.02438 5.5225 -4.24313 5.04875 -3.88672 cu +5.04875 -3.88672 4.57516 -3.53031 3.8575 -3.53031 cu +3.8575 -3.53031 3.03219 -3.53031 2.47797 -3.92578 cu +2.47797 -3.92578 1.92391 -4.32125 1.69922 -4.98781 cu +1.69922 -4.98781 1.47469 -5.65422 1.47469 -6.36234 cu +1.47469 -6.36234 1.47469 -7.27531 1.74078 -7.95656 cu +1.74078 -7.95656 2.00687 -8.63766 2.56828 -8.97469 cu +2.56828 -8.97469 3.12984 -9.31156 3.78422 -9.31156 cu +3.78422 -9.31156 4.58016 -9.31156 5.13187 -8.85266 cu +5.13187 -8.85266 5.68359 -8.39359 5.87891 -7.49016 cu +cl +} bind def +/G39 { newpath +0.771563 -10 m +0.771563 -10 0.771563 -2.84172 0.771563 -2.84172 cu +0.771563 -2.84172 3.23734 -2.84172 3.23734 -2.84172 cu +3.23734 -2.84172 4.07234 -2.84172 4.51172 -2.94437 cu +4.51172 -2.94437 5.12703 -3.08594 5.56156 -3.45703 cu +5.56156 -3.45703 6.12797 -3.93547 6.40875 -4.68016 cu +6.40875 -4.68016 6.68953 -5.42484 6.68953 -6.38187 cu +6.68953 -6.38187 6.68953 -7.19719 6.49906 -7.82719 cu +6.49906 -7.82719 6.30859 -8.45703 6.01078 -8.86969 cu +6.01078 -8.86969 5.71297 -9.28219 5.35891 -9.51906 cu +5.35891 -9.51906 5.00484 -9.75578 4.50438 -9.87797 cu +4.50438 -9.87797 4.00391 -10 3.35453 -10 cu +3.35453 -10 0.771563 -10 0.771563 -10 cu +0.771563 -10 0.771563 -10 0.771563 -10 cu +cl +1.71875 -9.15531 m +1.71875 -9.15531 3.24703 -9.15531 3.24703 -9.15531 cu +3.24703 -9.15531 3.95516 -9.15531 4.35797 -9.02344 cu +4.35797 -9.02344 4.76078 -8.89156 5 -8.65234 cu +5 -8.65234 5.33688 -8.31547 5.52484 -7.74656 cu +5.52484 -7.74656 5.71297 -7.17766 5.71297 -6.36719 cu +5.71297 -6.36719 5.71297 -5.24406 5.34422 -4.64109 cu +5.34422 -4.64109 4.97562 -4.03812 4.44828 -3.83297 cu +4.44828 -3.83297 4.06734 -3.68656 3.22266 -3.68656 cu +3.22266 -3.68656 1.71875 -3.68656 1.71875 -3.68656 cu +1.71875 -3.68656 1.71875 -9.15531 1.71875 -9.15531 cu +cl +} bind def +/G44 { newpath +0.932656 -10 m +0.932656 -10 0.932656 -2.84172 0.932656 -2.84172 cu +0.932656 -2.84172 1.87984 -2.84172 1.87984 -2.84172 cu +1.87984 -2.84172 1.87984 -10 1.87984 -10 cu +1.87984 -10 0.932656 -10 0.932656 -10 cu +cl +} bind def +/G48 { newpath +0.742188 -10 m +0.742188 -10 0.742188 -2.84172 0.742188 -2.84172 cu +0.742188 -2.84172 2.16797 -2.84172 2.16797 -2.84172 cu +2.16797 -2.84172 3.86234 -7.91016 3.86234 -7.91016 cu +3.86234 -7.91016 4.09672 -8.61812 4.20406 -8.96969 cu +4.20406 -8.96969 4.32625 -8.57906 4.585 -7.82219 cu +4.585 -7.82219 6.29891 -2.84172 6.29891 -2.84172 cu +6.29891 -2.84172 7.57328 -2.84172 7.57328 -2.84172 cu +7.57328 -2.84172 7.57328 -10 7.57328 -10 cu +7.57328 -10 6.66016 -10 6.66016 -10 cu +6.66016 -10 6.66016 -4.00875 6.66016 -4.00875 cu +6.66016 -4.00875 4.58016 -10 4.58016 -10 cu +4.58016 -10 3.72562 -10 3.72562 -10 cu +3.72562 -10 1.65531 -3.90625 1.65531 -3.90625 cu +1.65531 -3.90625 1.65531 -10 1.65531 -10 cu +1.65531 -10 0.742188 -10 0.742188 -10 cu +cl +} bind def +/G49 { newpath +0.761719 -10 m +0.761719 -10 0.761719 -2.84172 0.761719 -2.84172 cu +0.761719 -2.84172 1.73344 -2.84172 1.73344 -2.84172 cu +1.73344 -2.84172 5.49313 -8.46187 5.49313 -8.46187 cu +5.49313 -8.46187 5.49313 -2.84172 5.49313 -2.84172 cu +5.49313 -2.84172 6.40141 -2.84172 6.40141 -2.84172 cu +6.40141 -2.84172 6.40141 -10 6.40141 -10 cu +6.40141 -10 5.42969 -10 5.42969 -10 cu +5.42969 -10 1.67 -4.375 1.67 -4.375 cu +1.67 -4.375 1.67 -10 1.67 -10 cu +1.67 -10 0.761719 -10 0.761719 -10 cu +cl +} bind def +/G51 { newpath +0.771563 -10 m +0.771563 -10 0.771563 -2.84172 0.771563 -2.84172 cu +0.771563 -2.84172 3.47172 -2.84172 3.47172 -2.84172 cu +3.47172 -2.84172 4.18453 -2.84172 4.56062 -2.91016 cu +4.56062 -2.91016 5.08797 -2.99797 5.44437 -3.24469 cu +5.44437 -3.24469 5.80078 -3.49125 6.01797 -3.93562 cu +6.01797 -3.93562 6.23531 -4.37984 6.23531 -4.91203 cu +6.23531 -4.91203 6.23531 -5.82516 5.65422 -6.4575 cu +5.65422 -6.4575 5.07328 -7.08984 3.55469 -7.08984 cu +3.55469 -7.08984 1.71875 -7.08984 1.71875 -7.08984 cu +1.71875 -7.08984 1.71875 -10 1.71875 -10 cu +1.71875 -10 0.771563 -10 0.771563 -10 cu +0.771563 -10 0.771563 -10 0.771563 -10 cu +cl +1.71875 -6.24516 m +1.71875 -6.24516 3.56937 -6.24516 3.56937 -6.24516 cu +3.56937 -6.24516 4.48734 -6.24516 4.87297 -5.90344 cu +4.87297 -5.90344 5.25875 -5.56156 5.25875 -4.94141 cu +5.25875 -4.94141 5.25875 -4.49219 5.03172 -4.17234 cu +5.03172 -4.17234 4.80469 -3.8525 4.43359 -3.75 cu +4.43359 -3.75 4.19437 -3.68656 3.54984 -3.68656 cu +3.54984 -3.68656 1.71875 -3.68656 1.71875 -3.68656 cu +1.71875 -3.68656 1.71875 -6.24516 1.71875 -6.24516 cu +cl +} bind def +/G53 { newpath +0.786094 -10 m +0.786094 -10 0.786094 -2.84172 0.786094 -2.84172 cu +0.786094 -2.84172 3.96 -2.84172 3.96 -2.84172 cu +3.96 -2.84172 4.91703 -2.84172 5.415 -3.03469 cu +5.415 -3.03469 5.91312 -3.2275 6.21094 -3.71578 cu +6.21094 -3.71578 6.50875 -4.20406 6.50875 -4.79484 cu +6.50875 -4.79484 6.50875 -5.55656 6.01562 -6.07906 cu +6.01562 -6.07906 5.5225 -6.60156 4.49219 -6.74313 cu +4.49219 -6.74313 4.86813 -6.92375 5.06344 -7.09953 cu +5.06344 -7.09953 5.47859 -7.48047 5.84969 -8.05172 cu +5.84969 -8.05172 7.09469 -10 7.09469 -10 cu +7.09469 -10 5.90328 -10 5.90328 -10 cu +5.90328 -10 4.95609 -8.51078 4.95609 -8.51078 cu +4.95609 -8.51078 4.54109 -7.86625 4.2725 -7.52453 cu +4.2725 -7.52453 4.00391 -7.18266 3.79141 -7.04594 cu +3.79141 -7.04594 3.57906 -6.90922 3.35938 -6.85547 cu +3.35938 -6.85547 3.19828 -6.82125 2.83203 -6.82125 cu +2.83203 -6.82125 1.73344 -6.82125 1.73344 -6.82125 cu +1.73344 -6.82125 1.73344 -10 1.73344 -10 cu +1.73344 -10 0.786094 -10 0.786094 -10 cu +0.786094 -10 0.786094 -10 0.786094 -10 cu +cl +1.73344 -6.00094 m +1.73344 -6.00094 3.76953 -6.00094 3.76953 -6.00094 cu +3.76953 -6.00094 4.41891 -6.00094 4.78516 -5.86672 cu +4.78516 -5.86672 5.15141 -5.73234 5.34172 -5.43703 cu +5.34172 -5.43703 5.53219 -5.14156 5.53219 -4.79484 cu +5.53219 -4.79484 5.53219 -4.28703 5.16359 -3.96 cu +5.16359 -3.96 4.795 -3.63281 3.99906 -3.63281 cu +3.99906 -3.63281 1.73344 -3.63281 1.73344 -3.63281 cu +1.73344 -3.63281 1.73344 -6.00094 1.73344 -6.00094 cu +cl +} bind def +/G54 { newpath +0.449219 -7.70016 m +0.449219 -7.70016 1.34281 -7.62203 1.34281 -7.62203 cu +1.34281 -7.62203 1.40625 -8.15922 1.63813 -8.50344 cu +1.63813 -8.50344 1.87016 -8.84766 2.35844 -9.06016 cu +2.35844 -9.06016 2.84672 -9.2725 3.45703 -9.2725 cu +3.45703 -9.2725 3.99906 -9.2725 4.41406 -9.11141 cu +4.41406 -9.11141 4.82906 -8.95016 5.03172 -8.66938 cu +5.03172 -8.66938 5.23438 -8.38859 5.23438 -8.05656 cu +5.23438 -8.05656 5.23438 -7.71969 5.03906 -7.46828 cu +5.03906 -7.46828 4.84375 -7.21672 4.39453 -7.04594 cu +4.39453 -7.04594 4.10641 -6.93359 3.12 -6.69688 cu +3.12 -6.69688 2.13375 -6.46 1.73828 -6.25 cu +1.73828 -6.25 1.22562 -5.98141 0.974063 -5.58344 cu +0.974063 -5.58344 0.722656 -5.18547 0.722656 -4.69234 cu +0.722656 -4.69234 0.722656 -4.15031 1.03031 -3.67922 cu +1.03031 -3.67922 1.33797 -3.20797 1.92875 -2.96391 cu +1.92875 -2.96391 2.51953 -2.71969 3.24219 -2.71969 cu +3.24219 -2.71969 4.03812 -2.71969 4.64594 -2.97609 cu +4.64594 -2.97609 5.25391 -3.23234 5.58109 -3.73047 cu +5.58109 -3.73047 5.90828 -4.22844 5.93266 -4.85844 cu +5.93266 -4.85844 5.02438 -4.92672 5.02438 -4.92672 cu +5.02438 -4.92672 4.95125 -4.24797 4.52875 -3.90141 cu +4.52875 -3.90141 4.10641 -3.55469 3.28125 -3.55469 cu +3.28125 -3.55469 2.42188 -3.55469 2.02875 -3.86969 cu +2.02875 -3.86969 1.63578 -4.18453 1.63578 -4.62891 cu +1.63578 -4.62891 1.63578 -5.01469 1.91406 -5.26359 cu +1.91406 -5.26359 2.1875 -5.51266 3.34219 -5.77391 cu +3.34219 -5.77391 4.49703 -6.03516 4.92672 -6.23047 cu +4.92672 -6.23047 5.55172 -6.51859 5.84953 -6.96047 cu +5.84953 -6.96047 6.1475 -7.40234 6.1475 -7.97844 cu +6.1475 -7.97844 6.1475 -8.54984 5.82031 -9.05516 cu +5.82031 -9.05516 5.49313 -9.56047 4.88031 -9.84125 cu +4.88031 -9.84125 4.26766 -10.122 3.50094 -10.122 cu +3.50094 -10.122 2.52937 -10.122 1.8725 -9.83891 cu +1.8725 -9.83891 1.21578 -9.55562 0.842187 -8.98687 cu +0.842187 -8.98687 0.46875 -8.41797 0.449219 -7.70016 cu +cl +} bind def +/G68 { newpath +4.04297 -9.36031 m +4.04297 -9.36031 3.55469 -9.77531 3.10297 -9.94625 cu +3.10297 -9.94625 2.65141 -10.1172 2.13375 -10.1172 cu +2.13375 -10.1172 1.27937 -10.1172 0.820312 -9.69969 cu +0.820312 -9.69969 0.361406 -9.28219 0.361406 -8.63281 cu +0.361406 -8.63281 0.361406 -8.25188 0.534687 -7.93703 cu +0.534687 -7.93703 0.707969 -7.62203 0.98875 -7.43172 cu +0.98875 -7.43172 1.26953 -7.24125 1.62109 -7.14359 cu +1.62109 -7.14359 1.87984 -7.07516 2.40234 -7.01172 cu +2.40234 -7.01172 3.46687 -6.88469 3.96969 -6.70891 cu +3.96969 -6.70891 3.97469 -6.52828 3.97469 -6.47953 cu +3.97469 -6.47953 3.97469 -5.94234 3.72562 -5.72266 cu +3.72562 -5.72266 3.38875 -5.42484 2.72469 -5.42484 cu +2.72469 -5.42484 2.10453 -5.42484 1.80906 -5.64219 cu +1.80906 -5.64219 1.51375 -5.85938 1.37203 -6.41109 cu +1.37203 -6.41109 0.512656 -6.29391 0.512656 -6.29391 cu +0.512656 -6.29391 0.629844 -5.74219 0.898438 -5.40281 cu +0.898438 -5.40281 1.16703 -5.06344 1.67484 -4.88031 cu +1.67484 -4.88031 2.18266 -4.69719 2.85156 -4.69719 cu +2.85156 -4.69719 3.51562 -4.69719 3.93063 -4.85344 cu +3.93063 -4.85344 4.34578 -5.00969 4.54109 -5.24656 cu +4.54109 -5.24656 4.73641 -5.48344 4.81453 -5.84469 cu +4.81453 -5.84469 4.85844 -6.06937 4.85844 -6.65531 cu +4.85844 -6.65531 4.85844 -7.82719 4.85844 -7.82719 cu +4.85844 -7.82719 4.85844 -9.05266 4.91453 -9.3775 cu +4.91453 -9.3775 4.97078 -9.70219 5.13672 -10 cu +5.13672 -10 4.21875 -10 4.21875 -10 cu +4.21875 -10 4.08203 -9.72656 4.04297 -9.36031 cu +4.04297 -9.36031 4.04297 -9.36031 4.04297 -9.36031 cu +cl +3.96969 -7.3975 m +3.96969 -7.3975 3.49125 -7.59281 2.53422 -7.72953 cu +2.53422 -7.72953 1.99219 -7.80766 1.7675 -7.90531 cu +1.7675 -7.90531 1.54297 -8.00297 1.42094 -8.19094 cu +1.42094 -8.19094 1.29891 -8.37891 1.29891 -8.60844 cu +1.29891 -8.60844 1.29891 -8.96 1.565 -9.19438 cu +1.565 -9.19438 1.83109 -9.42875 2.34375 -9.42875 cu +2.34375 -9.42875 2.85156 -9.42875 3.24703 -9.20656 cu +3.24703 -9.20656 3.64266 -8.98438 3.82812 -8.59859 cu +3.82812 -8.59859 3.96969 -8.30078 3.96969 -7.71969 cu +3.96969 -7.71969 3.96969 -7.3975 3.96969 -7.3975 cu +cl +} bind def +/G69 { newpath +1.46969 -10 m +1.46969 -10 0.654375 -10 0.654375 -10 cu +0.654375 -10 0.654375 -2.84172 0.654375 -2.84172 cu +0.654375 -2.84172 1.53328 -2.84172 1.53328 -2.84172 cu +1.53328 -2.84172 1.53328 -5.39547 1.53328 -5.39547 cu +1.53328 -5.39547 2.08984 -4.69719 2.95406 -4.69719 cu +2.95406 -4.69719 3.43266 -4.69719 3.85984 -4.89016 cu +3.85984 -4.89016 4.28719 -5.08297 4.56297 -5.43219 cu +4.56297 -5.43219 4.83891 -5.78125 4.99516 -6.27438 cu +4.99516 -6.27438 5.15141 -6.7675 5.15141 -7.32906 cu +5.15141 -7.32906 5.15141 -8.66203 4.49219 -9.38969 cu +4.49219 -9.38969 3.83297 -10.1172 2.91016 -10.1172 cu +2.91016 -10.1172 1.99219 -10.1172 1.46969 -9.35063 cu +1.46969 -9.35063 1.46969 -10 1.46969 -10 cu +1.46969 -10 1.46969 -10 1.46969 -10 cu +cl +1.46 -7.36813 m +1.46 -7.36813 1.46 -8.30078 1.71391 -8.71578 cu +1.71391 -8.71578 2.12891 -9.39453 2.83688 -9.39453 cu +2.83688 -9.39453 3.41312 -9.39453 3.83297 -8.89406 cu +3.83297 -8.89406 4.25297 -8.39359 4.25297 -7.40234 cu +4.25297 -7.40234 4.25297 -6.38672 3.85016 -5.90328 cu +3.85016 -5.90328 3.44734 -5.41984 2.87594 -5.41984 cu +2.87594 -5.41984 2.29984 -5.41984 1.87984 -5.92047 cu +1.87984 -5.92047 1.46 -6.42094 1.46 -7.36813 cu +cl +} bind def +/G70 { newpath +4.04297 -8.10063 m +4.04297 -8.10063 4.90719 -8.21281 4.90719 -8.21281 cu +4.90719 -8.21281 4.76562 -9.10641 4.18203 -9.61187 cu +4.18203 -9.61187 3.59859 -10.1172 2.74906 -10.1172 cu +2.74906 -10.1172 1.68453 -10.1172 1.0375 -9.42141 cu +1.0375 -9.42141 0.390625 -8.72563 0.390625 -7.42672 cu +0.390625 -7.42672 0.390625 -6.58688 0.668906 -5.95703 cu +0.668906 -5.95703 0.947344 -5.32719 1.51609 -5.01219 cu +1.51609 -5.01219 2.085 -4.69719 2.75391 -4.69719 cu +2.75391 -4.69719 3.59859 -4.69719 4.13563 -5.12453 cu +4.13563 -5.12453 4.67281 -5.55172 4.82422 -6.33781 cu +4.82422 -6.33781 3.96969 -6.46969 3.96969 -6.46969 cu +3.96969 -6.46969 3.84766 -5.94719 3.5375 -5.68359 cu +3.5375 -5.68359 3.2275 -5.41984 2.78812 -5.41984 cu +2.78812 -5.41984 2.12406 -5.41984 1.70891 -5.89594 cu +1.70891 -5.89594 1.29391 -6.37203 1.29391 -7.40234 cu +1.29391 -7.40234 1.29391 -8.44719 1.69422 -8.92094 cu +1.69422 -8.92094 2.09469 -9.39453 2.73922 -9.39453 cu +2.73922 -9.39453 3.25687 -9.39453 3.60344 -9.07719 cu +3.60344 -9.07719 3.95016 -8.75969 4.04297 -8.10063 cu +cl +} bind def +/G71 { newpath +4.02344 -10 m +4.02344 -10 4.02344 -9.34563 4.02344 -9.34563 cu +4.02344 -9.34563 3.53031 -10.1172 2.57328 -10.1172 cu +2.57328 -10.1172 1.95312 -10.1172 1.43312 -9.77547 cu +1.43312 -9.77547 0.913125 -9.43359 0.6275 -8.82078 cu +0.6275 -8.82078 0.341875 -8.20797 0.341875 -7.41203 cu +0.341875 -7.41203 0.341875 -6.63578 0.600625 -6.00344 cu +0.600625 -6.00344 0.859375 -5.37109 1.37688 -5.03422 cu +1.37688 -5.03422 1.89453 -4.69719 2.53422 -4.69719 cu +2.53422 -4.69719 3.00297 -4.69719 3.36906 -4.895 cu +3.36906 -4.895 3.73531 -5.09281 3.96484 -5.41016 cu +3.96484 -5.41016 3.96484 -2.84172 3.96484 -2.84172 cu +3.96484 -2.84172 4.83891 -2.84172 4.83891 -2.84172 cu +4.83891 -2.84172 4.83891 -10 4.83891 -10 cu +4.83891 -10 4.02344 -10 4.02344 -10 cu +4.02344 -10 4.02344 -10 4.02344 -10 cu +cl +1.24516 -7.41203 m +1.24516 -7.41203 1.24516 -8.40813 1.665 -8.90141 cu +1.665 -8.90141 2.085 -9.39453 2.65625 -9.39453 cu +2.65625 -9.39453 3.2325 -9.39453 3.63531 -8.92344 cu +3.63531 -8.92344 4.03812 -8.45219 4.03812 -7.48531 cu +4.03812 -7.48531 4.03812 -6.42094 3.62797 -5.92297 cu +3.62797 -5.92297 3.21781 -5.42484 2.61719 -5.42484 cu +2.61719 -5.42484 2.03125 -5.42484 1.63813 -5.90344 cu +1.63813 -5.90344 1.24516 -6.38187 1.24516 -7.41203 cu +cl +} bind def +/G72 { newpath +4.20906 -8.33 m +4.20906 -8.33 5.11719 -8.44234 5.11719 -8.44234 cu +5.11719 -8.44234 4.90234 -9.23828 4.32125 -9.67781 cu +4.32125 -9.67781 3.74031 -10.1172 2.83688 -10.1172 cu +2.83688 -10.1172 1.69922 -10.1172 1.03266 -9.41656 cu +1.03266 -9.41656 0.36625 -8.71578 0.36625 -7.45109 cu +0.36625 -7.45109 0.36625 -6.1425 1.04 -5.41984 cu +1.04 -5.41984 1.71391 -4.69719 2.78812 -4.69719 cu +2.78812 -4.69719 3.82812 -4.69719 4.48734 -5.40531 cu +4.48734 -5.40531 5.14656 -6.11328 5.14656 -7.3975 cu +5.14656 -7.3975 5.14656 -7.47562 5.14156 -7.63187 cu +5.14156 -7.63187 1.27438 -7.63187 1.27438 -7.63187 cu +1.27438 -7.63187 1.32328 -8.48625 1.75781 -8.94047 cu +1.75781 -8.94047 2.19234 -9.39453 2.84187 -9.39453 cu +2.84187 -9.39453 3.32516 -9.39453 3.66688 -9.14062 cu +3.66688 -9.14062 4.00875 -8.88672 4.20906 -8.33 cu +4.20906 -8.33 4.20906 -8.33 4.20906 -8.33 cu +cl +1.32328 -6.90922 m +1.32328 -6.90922 4.21875 -6.90922 4.21875 -6.90922 cu +4.21875 -6.90922 4.16016 -6.25484 3.88672 -5.92766 cu +3.88672 -5.92766 3.46687 -5.41984 2.79781 -5.41984 cu +2.79781 -5.41984 2.19234 -5.41984 1.77969 -5.82516 cu +1.77969 -5.82516 1.36719 -6.23047 1.32328 -6.90922 cu +cl +} bind def +/G73 { newpath +0.869219 -10 m +0.869219 -10 0.869219 -5.49797 0.869219 -5.49797 cu +0.869219 -5.49797 0.0928125 -5.49797 0.0928125 -5.49797 cu +0.0928125 -5.49797 0.0928125 -4.81438 0.0928125 -4.81438 cu +0.0928125 -4.81438 0.869219 -4.81438 0.869219 -4.81438 cu +0.869219 -4.81438 0.869219 -4.26266 0.869219 -4.26266 cu +0.869219 -4.26266 0.869219 -3.74016 0.961875 -3.48625 cu +0.961875 -3.48625 1.08891 -3.14453 1.40875 -2.93219 cu +1.40875 -2.93219 1.72859 -2.71969 2.30469 -2.71969 cu +2.30469 -2.71969 2.67578 -2.71969 3.125 -2.80766 cu +3.125 -2.80766 2.99313 -3.57422 2.99313 -3.57422 cu +2.99313 -3.57422 2.71969 -3.52531 2.47562 -3.52531 cu +2.47562 -3.52531 2.07516 -3.52531 1.90906 -3.69625 cu +1.90906 -3.69625 1.74313 -3.86719 1.74313 -4.33594 cu +1.74313 -4.33594 1.74313 -4.81438 1.74313 -4.81438 cu +1.74313 -4.81438 2.75391 -4.81438 2.75391 -4.81438 cu +2.75391 -4.81438 2.75391 -5.49797 2.75391 -5.49797 cu +2.75391 -5.49797 1.74313 -5.49797 1.74313 -5.49797 cu +1.74313 -5.49797 1.74313 -10 1.74313 -10 cu +1.74313 -10 0.869219 -10 0.869219 -10 cu +cl +} bind def +/G74 { newpath +0.498125 -10.4297 m +0.498125 -10.4297 1.3525 -10.5567 1.3525 -10.5567 cu +1.3525 -10.5567 1.40625 -10.9522 1.65047 -11.1328 cu +1.65047 -11.1328 1.9775 -11.377 2.54391 -11.377 cu +2.54391 -11.377 3.15437 -11.377 3.48641 -11.1328 cu +3.48641 -11.1328 3.81844 -10.8887 3.93562 -10.4492 cu +3.93562 -10.4492 4.00391 -10.1806 3.99906 -9.32125 cu +3.99906 -9.32125 3.42281 -10 2.56344 -10 cu +2.56344 -10 1.49422 -10 0.908281 -9.22859 cu +0.908281 -9.22859 0.322344 -8.45703 0.322344 -7.37797 cu +0.322344 -7.37797 0.322344 -6.63578 0.590781 -6.00828 cu +0.590781 -6.00828 0.859375 -5.38078 1.36953 -5.03906 cu +1.36953 -5.03906 1.87984 -4.69719 2.56844 -4.69719 cu +2.56844 -4.69719 3.48641 -4.69719 4.08203 -5.43938 cu +4.08203 -5.43938 4.08203 -4.81438 4.08203 -4.81438 cu +4.08203 -4.81438 4.89266 -4.81438 4.89266 -4.81438 cu +4.89266 -4.81438 4.89266 -9.29688 4.89266 -9.29688 cu +4.89266 -9.29688 4.89266 -10.5078 4.64594 -11.0131 cu +4.64594 -11.0131 4.39938 -11.5186 3.86469 -11.8116 cu +3.86469 -11.8116 3.33016 -12.1045 2.54891 -12.1045 cu +2.54891 -12.1045 1.62109 -12.1045 1.04984 -11.687 cu +1.04984 -11.687 0.478594 -11.2695 0.498125 -10.4297 cu +0.498125 -10.4297 0.498125 -10.4297 0.498125 -10.4297 cu +cl +1.22562 -7.31438 m +1.22562 -7.31438 1.22562 -8.335 1.63078 -8.80375 cu +1.63078 -8.80375 2.03609 -9.2725 2.64656 -9.2725 cu +2.64656 -9.2725 3.25203 -9.2725 3.66219 -8.80625 cu +3.66219 -8.80625 4.07234 -8.33984 4.07234 -7.34375 cu +4.07234 -7.34375 4.07234 -6.39156 3.64984 -5.90828 cu +3.64984 -5.90828 3.2275 -5.42484 2.63187 -5.42484 cu +2.63187 -5.42484 2.04594 -5.42484 1.63578 -5.90094 cu +1.63578 -5.90094 1.22562 -6.37688 1.22562 -7.31438 cu +cl +} bind def +/G75 { newpath +0.659219 -10 m +0.659219 -10 0.659219 -2.84172 0.659219 -2.84172 cu +0.659219 -2.84172 1.53812 -2.84172 1.53812 -2.84172 cu +1.53812 -2.84172 1.53812 -5.41016 1.53812 -5.41016 cu +1.53812 -5.41016 2.15328 -4.69719 3.09078 -4.69719 cu +3.09078 -4.69719 3.66703 -4.69719 4.09172 -4.92437 cu +4.09172 -4.92437 4.51656 -5.15141 4.69969 -5.55187 cu +4.69969 -5.55187 4.88281 -5.95219 4.88281 -6.71391 cu +4.88281 -6.71391 4.88281 -10 4.88281 -10 cu +4.88281 -10 4.00391 -10 4.00391 -10 cu +4.00391 -10 4.00391 -6.71391 4.00391 -6.71391 cu +4.00391 -6.71391 4.00391 -6.05469 3.71828 -5.75437 cu +3.71828 -5.75437 3.43266 -5.45406 2.91016 -5.45406 cu +2.91016 -5.45406 2.51953 -5.45406 2.17531 -5.65672 cu +2.17531 -5.65672 1.83109 -5.85938 1.68453 -6.20609 cu +1.68453 -6.20609 1.53812 -6.55266 1.53812 -7.16312 cu +1.53812 -7.16312 1.53812 -10 1.53812 -10 cu +1.53812 -10 0.659219 -10 0.659219 -10 cu +cl +} bind def +/G76 { newpath +0.664062 -3.8525 m +0.664062 -3.8525 0.664062 -2.84172 0.664062 -2.84172 cu +0.664062 -2.84172 1.54297 -2.84172 1.54297 -2.84172 cu +1.54297 -2.84172 1.54297 -3.8525 1.54297 -3.8525 cu +1.54297 -3.8525 0.664062 -3.8525 0.664062 -3.8525 cu +0.664062 -3.8525 0.664062 -3.8525 0.664062 -3.8525 cu +cl +0.664062 -10 m +0.664062 -10 0.664062 -4.81438 0.664062 -4.81438 cu +0.664062 -4.81438 1.54297 -4.81438 1.54297 -4.81438 cu +1.54297 -4.81438 1.54297 -10 1.54297 -10 cu +1.54297 -10 0.664062 -10 0.664062 -10 cu +cl +} bind def +/G78 { newpath +0.664062 -10 m +0.664062 -10 0.664062 -2.84172 0.664062 -2.84172 cu +0.664062 -2.84172 1.54297 -2.84172 1.54297 -2.84172 cu +1.54297 -2.84172 1.54297 -6.92375 1.54297 -6.92375 cu +1.54297 -6.92375 3.62312 -4.81438 3.62312 -4.81438 cu +3.62312 -4.81438 4.76078 -4.81438 4.76078 -4.81438 cu +4.76078 -4.81438 2.77828 -6.73828 2.77828 -6.73828 cu +2.77828 -6.73828 4.96094 -10 4.96094 -10 cu +4.96094 -10 3.87703 -10 3.87703 -10 cu +3.87703 -10 2.16312 -7.34859 2.16312 -7.34859 cu +2.16312 -7.34859 1.54297 -7.94437 1.54297 -7.94437 cu +1.54297 -7.94437 1.54297 -10 1.54297 -10 cu +1.54297 -10 0.664062 -10 0.664062 -10 cu +cl +} bind def +/G79 { newpath +0.639687 -10 m +0.639687 -10 0.639687 -2.84172 0.639687 -2.84172 cu +0.639687 -2.84172 1.51859 -2.84172 1.51859 -2.84172 cu +1.51859 -2.84172 1.51859 -10 1.51859 -10 cu +1.51859 -10 0.639687 -10 0.639687 -10 cu +cl +} bind def +/G80 { newpath +0.659219 -10 m +0.659219 -10 0.659219 -4.81438 0.659219 -4.81438 cu +0.659219 -4.81438 1.44531 -4.81438 1.44531 -4.81438 cu +1.44531 -4.81438 1.44531 -5.54203 1.44531 -5.54203 cu +1.44531 -5.54203 1.68953 -5.16109 2.09469 -4.92922 cu +2.09469 -4.92922 2.5 -4.69719 3.01766 -4.69719 cu +3.01766 -4.69719 3.59375 -4.69719 3.96234 -4.93656 cu +3.96234 -4.93656 4.33109 -5.17578 4.4825 -5.60547 cu +4.4825 -5.60547 5.09766 -4.69719 6.08406 -4.69719 cu +6.08406 -4.69719 6.85547 -4.69719 7.27047 -5.12453 cu +7.27047 -5.12453 7.68562 -5.55172 7.68562 -6.44047 cu +7.68562 -6.44047 7.68562 -10 7.68562 -10 cu +7.68562 -10 6.81156 -10 6.81156 -10 cu +6.81156 -10 6.81156 -6.73344 6.81156 -6.73344 cu +6.81156 -6.73344 6.81156 -6.20609 6.72609 -5.97422 cu +6.72609 -5.97422 6.64062 -5.74219 6.41594 -5.60062 cu +6.41594 -5.60062 6.19141 -5.45891 5.88875 -5.45891 cu +5.88875 -5.45891 5.34187 -5.45891 4.98047 -5.82281 cu +4.98047 -5.82281 4.61922 -6.18656 4.61922 -6.98734 cu +4.61922 -6.98734 4.61922 -10 4.61922 -10 cu +4.61922 -10 3.74031 -10 3.74031 -10 cu +3.74031 -10 3.74031 -6.63078 3.74031 -6.63078 cu +3.74031 -6.63078 3.74031 -6.04484 3.52547 -5.75188 cu +3.52547 -5.75188 3.31062 -5.45891 2.82234 -5.45891 cu +2.82234 -5.45891 2.45125 -5.45891 2.13625 -5.65422 cu +2.13625 -5.65422 1.82125 -5.84953 1.67969 -6.22562 cu +1.67969 -6.22562 1.53812 -6.60156 1.53812 -7.30953 cu +1.53812 -7.30953 1.53812 -10 1.53812 -10 cu +1.53812 -10 0.659219 -10 0.659219 -10 cu +cl +} bind def +/G81 { newpath +0.659219 -10 m +0.659219 -10 0.659219 -4.81438 0.659219 -4.81438 cu +0.659219 -4.81438 1.45016 -4.81438 1.45016 -4.81438 cu +1.45016 -4.81438 1.45016 -5.55172 1.45016 -5.55172 cu +1.45016 -5.55172 2.02156 -4.69719 3.10062 -4.69719 cu +3.10062 -4.69719 3.56937 -4.69719 3.96234 -4.86578 cu +3.96234 -4.86578 4.35547 -5.03422 4.55078 -5.30766 cu +4.55078 -5.30766 4.74609 -5.58109 4.82422 -5.95703 cu +4.82422 -5.95703 4.87312 -6.20109 4.87312 -6.81156 cu +4.87312 -6.81156 4.87312 -10 4.87312 -10 cu +4.87312 -10 3.99422 -10 3.99422 -10 cu +3.99422 -10 3.99422 -6.84563 3.99422 -6.84563 cu +3.99422 -6.84563 3.99422 -6.30859 3.89156 -6.0425 cu +3.89156 -6.0425 3.78906 -5.77641 3.52781 -5.61766 cu +3.52781 -5.61766 3.26656 -5.45891 2.915 -5.45891 cu +2.915 -5.45891 2.35359 -5.45891 1.94578 -5.81547 cu +1.94578 -5.81547 1.53812 -6.17188 1.53812 -7.16797 cu +1.53812 -7.16797 1.53812 -10 1.53812 -10 cu +1.53812 -10 0.659219 -10 0.659219 -10 cu +cl +} bind def +/G82 { newpath +0.332031 -7.40719 m +0.332031 -7.40719 0.332031 -5.96672 1.13281 -5.27344 cu +1.13281 -5.27344 1.80172 -4.69719 2.76375 -4.69719 cu +2.76375 -4.69719 3.83297 -4.69719 4.51172 -5.39797 cu +4.51172 -5.39797 5.19047 -6.09859 5.19047 -7.33391 cu +5.19047 -7.33391 5.19047 -8.335 4.89016 -8.90875 cu +4.89016 -8.90875 4.58984 -9.48234 4.01609 -9.79984 cu +4.01609 -9.79984 3.44234 -10.1172 2.76375 -10.1172 cu +2.76375 -10.1172 1.67484 -10.1172 1.00344 -9.41891 cu +1.00344 -9.41891 0.332031 -8.72063 0.332031 -7.40719 cu +0.332031 -7.40719 0.332031 -7.40719 0.332031 -7.40719 cu +cl +1.23531 -7.40719 m +1.23531 -7.40719 1.23531 -8.40328 1.66984 -8.89891 cu +1.66984 -8.89891 2.10453 -9.39453 2.76375 -9.39453 cu +2.76375 -9.39453 3.41797 -9.39453 3.8525 -8.89656 cu +3.8525 -8.89656 4.28719 -8.39844 4.28719 -7.37797 cu +4.28719 -7.37797 4.28719 -6.41594 3.85016 -5.92047 cu +3.85016 -5.92047 3.41312 -5.42484 2.76375 -5.42484 cu +2.76375 -5.42484 2.10453 -5.42484 1.66984 -5.91797 cu +1.66984 -5.91797 1.23531 -6.41109 1.23531 -7.40719 cu +cl +} bind def +/G83 { newpath +0.659219 -11.9873 m +0.659219 -11.9873 0.659219 -4.81438 0.659219 -4.81438 cu +0.659219 -4.81438 1.46 -4.81438 1.46 -4.81438 cu +1.46 -4.81438 1.46 -5.48828 1.46 -5.48828 cu +1.46 -5.48828 1.74313 -5.09281 2.09953 -4.895 cu +2.09953 -4.895 2.45609 -4.69719 2.96391 -4.69719 cu +2.96391 -4.69719 3.62797 -4.69719 4.13578 -5.03906 cu +4.13578 -5.03906 4.64359 -5.38078 4.90234 -6.00344 cu +4.90234 -6.00344 5.16109 -6.62594 5.16109 -7.36813 cu +5.16109 -7.36813 5.16109 -8.16406 4.87547 -8.80125 cu +4.87547 -8.80125 4.58984 -9.43844 4.04531 -9.77781 cu +4.04531 -9.77781 3.50094 -10.1172 2.90047 -10.1172 cu +2.90047 -10.1172 2.46094 -10.1172 2.11172 -9.93172 cu +2.11172 -9.93172 1.76266 -9.74609 1.53812 -9.46281 cu +1.53812 -9.46281 1.53812 -11.9873 1.53812 -11.9873 cu +1.53812 -11.9873 0.659219 -11.9873 0.659219 -11.9873 cu +0.659219 -11.9873 0.659219 -11.9873 0.659219 -11.9873 cu +cl +1.45516 -7.43656 m +1.45516 -7.43656 1.45516 -8.4375 1.86031 -8.91609 cu +1.86031 -8.91609 2.26562 -9.39453 2.84187 -9.39453 cu +2.84187 -9.39453 3.42781 -9.39453 3.84516 -8.89891 cu +3.84516 -8.89891 4.26266 -8.40328 4.26266 -7.36328 cu +4.26266 -7.36328 4.26266 -6.37203 3.855 -5.87891 cu +3.855 -5.87891 3.44734 -5.38578 2.88094 -5.38578 cu +2.88094 -5.38578 2.31937 -5.38578 1.88719 -5.91062 cu +1.88719 -5.91062 1.45516 -6.43547 1.45516 -7.43656 cu +cl +} bind def +/G85 { newpath +0.649375 -10 m +0.649375 -10 0.649375 -4.81438 0.649375 -4.81438 cu +0.649375 -4.81438 1.44047 -4.81438 1.44047 -4.81438 cu +1.44047 -4.81438 1.44047 -5.60062 1.44047 -5.60062 cu +1.44047 -5.60062 1.74313 -5.04875 1.99953 -4.87297 cu +1.99953 -4.87297 2.25594 -4.69719 2.56344 -4.69719 cu +2.56344 -4.69719 3.00781 -4.69719 3.46687 -4.98047 cu +3.46687 -4.98047 3.16406 -5.79594 3.16406 -5.79594 cu +3.16406 -5.79594 2.84187 -5.60547 2.51953 -5.60547 cu +2.51953 -5.60547 2.23141 -5.60547 2.00188 -5.77891 cu +2.00188 -5.77891 1.7725 -5.95219 1.67484 -6.25969 cu +1.67484 -6.25969 1.52828 -6.72844 1.52828 -7.28516 cu +1.52828 -7.28516 1.52828 -10 1.52828 -10 cu +1.52828 -10 0.649375 -10 0.649375 -10 cu +cl +} bind def +/G86 { newpath +0.307656 -8.45219 m +0.307656 -8.45219 1.17672 -8.31547 1.17672 -8.31547 cu +1.17672 -8.31547 1.25 -8.83781 1.58438 -9.11625 cu +1.58438 -9.11625 1.91891 -9.39453 2.51953 -9.39453 cu +2.51953 -9.39453 3.125 -9.39453 3.41797 -9.14797 cu +3.41797 -9.14797 3.71094 -8.90141 3.71094 -8.56938 cu +3.71094 -8.56938 3.71094 -8.27141 3.45219 -8.10063 cu +3.45219 -8.10063 3.27156 -7.98344 2.55375 -7.80266 cu +2.55375 -7.80266 1.58688 -7.55859 1.21328 -7.38047 cu +1.21328 -7.38047 0.839844 -7.20219 0.646875 -6.88719 cu +0.646875 -6.88719 0.454063 -6.57219 0.454063 -6.19141 cu +0.454063 -6.19141 0.454063 -5.84469 0.612812 -5.54937 cu +0.612812 -5.54937 0.771563 -5.25391 1.045 -5.05859 cu +1.045 -5.05859 1.25 -4.90719 1.60391 -4.80219 cu +1.60391 -4.80219 1.95797 -4.69719 2.36328 -4.69719 cu +2.36328 -4.69719 2.97359 -4.69719 3.435 -4.87297 cu +3.435 -4.87297 3.89656 -5.04875 4.11625 -5.34906 cu +4.11625 -5.34906 4.33594 -5.64938 4.41891 -6.15234 cu +4.41891 -6.15234 3.55953 -6.26953 3.55953 -6.26953 cu +3.55953 -6.26953 3.50094 -5.86906 3.22016 -5.64453 cu +3.22016 -5.64453 2.93953 -5.41984 2.42672 -5.41984 cu +2.42672 -5.41984 1.82125 -5.41984 1.5625 -5.62016 cu +1.5625 -5.62016 1.30375 -5.82031 1.30375 -6.08891 cu +1.30375 -6.08891 1.30375 -6.25969 1.41109 -6.39641 cu +1.41109 -6.39641 1.51859 -6.53812 1.74812 -6.63078 cu +1.74812 -6.63078 1.87984 -6.67969 2.52438 -6.85547 cu +2.52438 -6.85547 3.45703 -7.10453 3.82563 -7.26328 cu +3.82563 -7.26328 4.19437 -7.42188 4.40422 -7.72469 cu +4.40422 -7.72469 4.61422 -8.02734 4.61422 -8.47656 cu +4.61422 -8.47656 4.61422 -8.91594 4.35781 -9.30422 cu +4.35781 -9.30422 4.10156 -9.69234 3.61813 -9.90484 cu +3.61813 -9.90484 3.13484 -10.1172 2.52438 -10.1172 cu +2.52438 -10.1172 1.51375 -10.1172 0.983906 -9.69734 cu +0.983906 -9.69734 0.454063 -9.27734 0.307656 -8.45219 cu +cl +} bind def +/G87 { newpath +2.57812 -9.21391 m +2.57812 -9.21391 2.70516 -9.99016 2.70516 -9.99016 cu +2.70516 -9.99016 2.33406 -10.0684 2.04109 -10.0684 cu +2.04109 -10.0684 1.5625 -10.0684 1.29875 -9.91703 cu +1.29875 -9.91703 1.03516 -9.76562 0.927656 -9.51906 cu +0.927656 -9.51906 0.820312 -9.2725 0.820312 -8.48141 cu +0.820312 -8.48141 0.820312 -5.49797 0.820312 -5.49797 cu +0.820312 -5.49797 0.175781 -5.49797 0.175781 -5.49797 cu +0.175781 -5.49797 0.175781 -4.81438 0.175781 -4.81438 cu +0.175781 -4.81438 0.820312 -4.81438 0.820312 -4.81438 cu +0.820312 -4.81438 0.820312 -3.53031 0.820312 -3.53031 cu +0.820312 -3.53031 1.69437 -3.00297 1.69437 -3.00297 cu +1.69437 -3.00297 1.69437 -4.81438 1.69437 -4.81438 cu +1.69437 -4.81438 2.57812 -4.81438 2.57812 -4.81438 cu +2.57812 -4.81438 2.57812 -5.49797 2.57812 -5.49797 cu +2.57812 -5.49797 1.69437 -5.49797 1.69437 -5.49797 cu +1.69437 -5.49797 1.69437 -8.53031 1.69437 -8.53031 cu +1.69437 -8.53031 1.69437 -8.90625 1.74078 -9.01375 cu +1.74078 -9.01375 1.78719 -9.12109 1.89203 -9.18453 cu +1.89203 -9.18453 1.99703 -9.24797 2.19234 -9.24797 cu +2.19234 -9.24797 2.33891 -9.24797 2.57812 -9.21391 cu +cl +} bind def +/G88 { newpath +4.05766 -10 m +4.05766 -10 4.05766 -9.23828 4.05766 -9.23828 cu +4.05766 -9.23828 3.45219 -10.1172 2.41219 -10.1172 cu +2.41219 -10.1172 1.95312 -10.1172 1.55516 -9.94141 cu +1.55516 -9.94141 1.15719 -9.76562 0.964375 -9.49953 cu +0.964375 -9.49953 0.771563 -9.23344 0.693438 -8.84766 cu +0.693438 -8.84766 0.639687 -8.58891 0.639687 -8.02734 cu +0.639687 -8.02734 0.639687 -4.81438 0.639687 -4.81438 cu +0.639687 -4.81438 1.51859 -4.81438 1.51859 -4.81438 cu +1.51859 -4.81438 1.51859 -7.69047 1.51859 -7.69047 cu +1.51859 -7.69047 1.51859 -8.37891 1.57234 -8.61812 cu +1.57234 -8.61812 1.65531 -8.96484 1.92375 -9.16266 cu +1.92375 -9.16266 2.19234 -9.36031 2.58797 -9.36031 cu +2.58797 -9.36031 2.98344 -9.36031 3.33 -9.15766 cu +3.33 -9.15766 3.67672 -8.955 3.82078 -8.60594 cu +3.82078 -8.60594 3.96484 -8.25688 3.96484 -7.59281 cu +3.96484 -7.59281 3.96484 -4.81438 3.96484 -4.81438 cu +3.96484 -4.81438 4.84375 -4.81438 4.84375 -4.81438 cu +4.84375 -4.81438 4.84375 -10 4.84375 -10 cu +4.84375 -10 4.05766 -10 4.05766 -10 cu +cl +} bind def +/G89 { newpath +2.09969 -10 m +2.09969 -10 0.127031 -4.81438 0.127031 -4.81438 cu +0.127031 -4.81438 1.05469 -4.81438 1.05469 -4.81438 cu +1.05469 -4.81438 2.16797 -7.91984 2.16797 -7.91984 cu +2.16797 -7.91984 2.34859 -8.42281 2.5 -8.96484 cu +2.5 -8.96484 2.61719 -8.55469 2.82719 -7.97844 cu +2.82719 -7.97844 3.97953 -4.81438 3.97953 -4.81438 cu +3.97953 -4.81438 4.88281 -4.81438 4.88281 -4.81438 cu +4.88281 -4.81438 2.92 -10 2.92 -10 cu +2.92 -10 2.09969 -10 2.09969 -10 cu +cl +} bind def +end +Scribusdict begin +%%EndSetup +%%Page: 1 1 +%%PageOrientation: Landscape +%%PageBoundingBox: 0 0 595 420 +%%PageCropBox: 0 0 595.28 419.53 +save +/DeviceCMYK setcolorspace +0 0 tr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +383 396.53 tr +0 0 m +0 0 196 0 196 0 curveto +196 0 196 -372 196 -372 curveto +196 -372 0 -372 0 -372 curveto +0 -372 0 0 0 0 curveto +cl +0 0 0.0980392 0.0392157 cmyk eofill +0 0 m +0 0 196 0 196 0 curveto +196 0 196 -372 196 -372 curveto +196 -372 0 -372 0 -372 curveto +0 -372 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1.9963 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +419 211.53 tr +0 0 m +0 0 116 0 116 0 curveto +116 0 116 -122 116 -122 curveto +116 -122 0 -122 0 -122 curveto +0 -122 0 0 0 0 curveto +cl +0 0.0862745 0.184314 0.196078 cmyk eofill +0 0 m +0 0 116 0 116 0 curveto +116 0 116 -122 116 -122 curveto +116 -122 0 -122 0 -122 curveto +0 -122 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1.9963 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +438.65 204.604 tr +gs +17.4229 1.26367 tr +0 0 0 1 cmyk (G54) ArialMT 1.2 shgf +gr +gs +25.4268 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +28.7607 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +35.4346 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +39.4307 1.26367 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +46.1045 1.26367 tr +0 0 0 1 cmyk (G74) ArialMT 1.2 shgf +gr +gs +52.7783 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +36 186.53 tr +144 -21 m +144 -32.5984 111.766 -42 72 -42 curveto +32.2355 -42 0 -32.5984 0 -21 curveto +0 -9.40202 32.2355 0 72 0 curveto +111.766 0 144 -9.40202 144 -21 curveto +cl +0.403922 0 0.137255 0.196078 cmyk eofill +144 -21 m +144 -32.5984 111.766 -42 72 -42 curveto +32.2355 -42 0 -32.5984 0 -21 curveto +0 -9.40202 32.2355 0 72 0 curveto +111.766 0 144 -9.40202 144 -21 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +58 173.53 tr +gs +1 1.26367 tr +0 0 0 1 cmyk (G36) ArialMT 1.2 shgf +gr +gs +9.00391 1.26367 tr +0 0 0 1 cmyk (G74) ArialMT 1.2 shgf +gr +gs +15.6777 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +22.3516 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +29.0254 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +35.6934 1.26367 tr +0 0 0 1 cmyk (G11) ArialMT 1.2 shgf +gr +gs +39.6895 1.26367 tr +0 0 0 1 cmyk (G53) ArialMT 1.2 shgf +gr +gs +48.3555 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +55.0293 1.26367 tr +0 0 0 1 cmyk (G86) ArialMT 1.2 shgf +gr +gs +61.0293 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +67.7031 1.26367 tr +0 0 0 1 cmyk (G88) ArialMT 1.2 shgf +gr +gs +74.377 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +78.373 1.26367 tr +0 0 0 1 cmyk (G70) ArialMT 1.2 shgf +gr +gs +84.373 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +91.0469 1.26367 tr +0 0 0 1 cmyk (G12) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +424 392.53 tr +0 0 m +0 0 107 0 107 0 curveto +107 0 107 -47 107 -47 curveto +107 -47 0 -47 0 -47 curveto +0 -47 0 0 0 0 curveto +cl +0.32549 0.0313725 0 0.196078 cmyk eofill +0 0 m +0 0 107 0 107 0 curveto +107 0 107 -47 107 -47 curveto +107 -47 0 -47 0 -47 curveto +0 -47 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +444 378.53 tr +gs +14.6582 1.26367 tr +0 0 0 1 cmyk (G38) ArialMT 1.2 shgf +gr +gs +23.3242 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +29.998 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +36.6719 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +40.0059 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +44.002 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +50.6758 1.26367 tr +0 0 0 1 cmyk (G79) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +258 239.53 tr +-145.751 ro +0 0 m +113.719 0 li +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +262 251.53 tr +-170.059 ro +0 0 m +98.4784 0 li +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +22 254.53 tr +144 -21 m +144 -32.5984 111.766 -42 72 -42 curveto +32.2355 -42 0 -32.5984 0 -21 curveto +0 -9.40202 32.2355 0 72 0 curveto +111.766 0 144 -9.40202 144 -21 curveto +cl +0.403922 0 0.137255 0.196078 cmyk eofill +144 -21 m +144 -32.5984 111.766 -42 72 -42 curveto +32.2355 -42 0 -32.5984 0 -21 curveto +0 -9.40202 32.2355 0 72 0 curveto +111.766 0 144 -9.40202 144 -21 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +44 241.53 tr +gs +1 1.26367 tr +0 0 0 1 cmyk (G36) ArialMT 1.2 shgf +gr +gs +9.00391 1.26367 tr +0 0 0 1 cmyk (G74) ArialMT 1.2 shgf +gr +gs +15.6777 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +22.3516 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +29.0254 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +35.6934 1.26367 tr +0 0 0 1 cmyk (G11) ArialMT 1.2 shgf +gr +gs +39.6895 1.26367 tr +0 0 0 1 cmyk (G53) ArialMT 1.2 shgf +gr +gs +48.3555 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +55.0293 1.26367 tr +0 0 0 1 cmyk (G86) ArialMT 1.2 shgf +gr +gs +61.0293 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +67.7031 1.26367 tr +0 0 0 1 cmyk (G88) ArialMT 1.2 shgf +gr +gs +74.377 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +78.373 1.26367 tr +0 0 0 1 cmyk (G70) ArialMT 1.2 shgf +gr +gs +84.373 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +91.0469 1.26367 tr +0 0 0 1 cmyk (G12) ArialMT 1.2 shgf +gr +gr +gs +430.5 346.03 m +430.5 346.03 524.5 346.03 524.5 346.03 curveto +524.5 346.03 524.5 319.03 524.5 319.03 curveto +524.5 319.03 430.5 319.03 430.5 319.03 curveto +430.5 319.03 430.5 346.03 430.5 346.03 curveto +cl +eoclip newpath +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +431 345.53 tr +0 0 m +0 0 93 0 93 0 curveto +93 0 93 -26 93 -26 curveto +93 -26 0 -26 0 -26 curveto +0 -26 0 0 0 0 curveto +cl +0.376471 0.0352941 0 0.0666667 cmyk eofill +0 0 m +0 0 93 0 93 0 curveto +93 0 93 -26 93 -26 curveto +93 -26 0 -26 0 -26 curveto +0 -26 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +436 341.53 tr +gs +1.65039 1.26367 tr +0 0 0 1 cmyk (G39) ArialMT 1.2 shgf +gr +gs +10.3164 1.26367 tr +0 0 0 1 cmyk (G16) ArialMT 1.2 shgf +gr +gs +14.3125 1.26367 tr +0 0 0 1 cmyk (G37) ArialMT 1.2 shgf +gr +gs +22.3164 1.26367 tr +0 0 0 1 cmyk (G88) ArialMT 1.2 shgf +gr +gs +28.9902 1.26367 tr +0 0 0 1 cmyk (G86) ArialMT 1.2 shgf +gr +gs +38.3242 1.26367 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +40.9902 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +47.6641 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +50.998 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +57.6719 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +61.668 1.26367 tr +0 0 0 1 cmyk (G73) ArialMT 1.2 shgf +gr +gs +65.002 1.26367 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +71.6758 1.26367 tr +0 0 0 1 cmyk (G70) ArialMT 1.2 shgf +gr +gs +77.6758 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +411 46.53 tr +gs +25.9785 1.26367 tr +0 0 0 1 cmyk (G36) ArialMT 1.2 shgf +gr +gs +33.9824 1.26367 tr +0 0 0 1 cmyk (G78) ArialMT 1.2 shgf +gr +gs +39.9824 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +46.6562 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +53.3301 1.26367 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +60.0039 1.26367 tr +0 0 0 1 cmyk (G71) ArialMT 1.2 shgf +gr +gs +66.6777 1.26367 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +72.6777 1.26367 tr +0 0 0 1 cmyk (G54) ArialMT 1.2 shgf +gr +gs +80.6816 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +87.3555 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +91.3516 1.26367 tr +0 0 0 1 cmyk (G89) ArialMT 1.2 shgf +gr +gs +97.3516 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +104.025 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +179 305.53 tr +0.516164 ro +0 0 m +111.005 0 li +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +154 364.53 tr +-18.9465 ro +0 0 m +141.676 0 li +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[6 2] 0 setdash +333 294.53 tr +11.8887 ro +0 0 m +116.499 0 li +0 0 0 1 cmyk st +gr +gs +1.25362 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +288 388.53 tr +0 0 m +0 0 46 0 46 0 curveto +46 0 46 -279 46 -279 curveto +46 -279 0 -279 0 -279 curveto +0 -279 0 0 0 0 curveto +cl +0.196078 0 0.196078 0.196078 cmyk eofill +0 0 m +0 0 46 0 46 0 curveto +46 0 46 -279 46 -279 curveto +46 -279 0 -279 0 -279 curveto +0 -279 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1.25362 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +301 198.171 tr +90 ro +gs +1 1.41016 tr +0 0 0 1 cmyk (G79) ArialMT 1.2 shgf +gr +gs +3.66602 1.41016 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +6.33203 1.41016 tr +0 0 0 1 cmyk (G69) ArialMT 1.2 shgf +gr +gs +13.0059 1.41016 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +19.6797 1.41016 tr +0 0 0 1 cmyk (G78) ArialMT 1.2 shgf +gr +gs +25.6797 1.41016 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +32.3535 1.41016 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +39.0273 1.41016 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +45.7012 1.41016 tr +0 0 0 1 cmyk (G71) ArialMT 1.2 shgf +gr +gs +52.375 1.41016 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +333 256.53 tr +-19.5367 ro +0 0 m +98.6813 0 li +0 0 0 1 cmyk st +gr +gs +420.45 241.08 m +420.45 241.08 533 241.08 533 241.08 curveto +533 241.08 533 210.981 533 210.981 curveto +533 210.981 420.45 210.981 420.45 210.981 curveto +420.45 210.981 420.45 241.08 420.45 241.08 curveto +cl +eoclip newpath +gs +1.11137 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +421.012 240.53 tr +0 0 m +0 0 111.426 0 111.426 0 curveto +111.426 0 111.426 -29 111.426 -29 curveto +111.426 -29 0 -29 0 -29 curveto +0 -29 0 0 0 0 curveto +cl +0 0.0980392 0.215686 0.0666667 cmyk eofill +0 0 m +0 0 111.426 0 111.426 0 curveto +111.426 0 111.426 -29 111.426 -29 curveto +111.426 -29 0 -29 0 -29 curveto +0 -29 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1.11137 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +426.528 234.085 tr +gs +12.0723 1.26367 tr +0 0 0 1 cmyk (G44) ArialMT 1.2 shgf +gr +gs +15.4062 1.26367 tr +0 0 0 1 cmyk (G48) ArialMT 1.2 shgf +gr +gs +25.4023 1.26367 tr +0 0 0 1 cmyk (G36) ArialMT 1.2 shgf +gr +gs +33.4062 1.26367 tr +0 0 0 1 cmyk (G51) ArialMT 1.2 shgf +gr +gs +44.5273 1.26367 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +47.1934 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +53.8672 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +57.2012 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +63.875 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +67.8711 1.26367 tr +0 0 0 1 cmyk (G73) ArialMT 1.2 shgf +gr +gs +71.2051 1.26367 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +77.8789 1.26367 tr +0 0 0 1 cmyk (G70) ArialMT 1.2 shgf +gr +gs +83.8789 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +256 195.53 tr +0 0 m +0 0 31 0 31 0 curveto +31 0 31 -72.7448 31 -72.7448 curveto +31 -72.7448 0 -72.7448 0 -72.7448 curveto +0 -72.7448 0 0 0 0 curveto +cl +0.27451 0 0.0901961 0.454902 cmyk eofill +0 0 m +0 0 31 0 31 0 curveto +31 0 31 -72.7448 31 -72.7448 curveto +31 -72.7448 0 -72.7448 0 -72.7448 curveto +0 -72.7448 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +262 136.53 tr +90 ro +gs +5.32324 1.41016 tr +0 0 0 1 cmyk (G79) ArialMT 1.2 shgf +gr +gs +7.98926 1.41016 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +10.6553 1.41016 tr +0 0 0 1 cmyk (G69) ArialMT 1.2 shgf +gr +gs +17.3291 1.41016 tr +0 0 0 1 cmyk (G78) ArialMT 1.2 shgf +gr +gs +23.3291 1.41016 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +30.0029 1.41016 tr +0 0 0 1 cmyk (G69) ArialMT 1.2 shgf +gr +gs +36.6768 1.41016 tr +0 0 0 1 cmyk (G70) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +124 120.53 tr +0 -13.75 m +0 -13.75 23 -13.75 23 -13.75 curveto +23 -13.75 23 0 23 0 curveto +23 0 69 0 69 0 curveto +69 0 69 -13.75 69 -13.75 curveto +69 -13.75 92 -13.75 92 -13.75 curveto +92 -13.75 92 -41.25 92 -41.25 curveto +92 -41.25 69 -41.25 69 -41.25 curveto +69 -41.25 69 -55 69 -55 curveto +69 -55 23 -55 23 -55 curveto +23 -55 23 -41.25 23 -41.25 curveto +23 -41.25 0 -41.25 0 -41.25 curveto +0 -41.25 0 -13.75 0 -13.75 curveto +cl +0.0588235 0 0 0 cmyk eofill +0 -13.75 m +0 -13.75 23 -13.75 23 -13.75 curveto +23 -13.75 23 0 23 0 curveto +23 0 69 0 69 0 curveto +69 0 69 -13.75 69 -13.75 curveto +69 -13.75 92 -13.75 92 -13.75 curveto +92 -13.75 92 -41.25 92 -41.25 curveto +92 -41.25 69 -41.25 69 -41.25 curveto +69 -41.25 69 -55 69 -55 curveto +69 -55 23 -55 23 -55 curveto +23 -55 23 -41.25 23 -41.25 curveto +23 -41.25 0 -41.25 0 -41.25 curveto +0 -41.25 0 -13.75 0 -13.75 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +132 100.53 tr +gs +6.64746 1.41016 tr +0 0 0 1 cmyk (G36) ArialMT 1.2 shgf +gr +gs +14.6514 1.41016 tr +0 0 0 1 cmyk (G83) ArialMT 1.2 shgf +gr +gs +21.3252 1.41016 tr +0 0 0 1 cmyk (G83) ArialMT 1.2 shgf +gr +gs +27.999 1.41016 tr +0 0 0 1 cmyk (G79) ArialMT 1.2 shgf +gr +gs +30.665 1.41016 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +33.3311 1.41016 tr +0 0 0 1 cmyk (G70) ArialMT 1.2 shgf +gr +gs +39.3311 1.41016 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +46.0049 1.41016 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +49.3389 1.41016 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +52.0049 1.41016 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +58.6787 1.41016 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +201 69.53 tr +0 -13.75 m +0 -13.75 23 -13.75 23 -13.75 curveto +23 -13.75 23 0 23 0 curveto +23 0 69 0 69 0 curveto +69 0 69 -13.75 69 -13.75 curveto +69 -13.75 92 -13.75 92 -13.75 curveto +92 -13.75 92 -41.25 92 -41.25 curveto +92 -41.25 69 -41.25 69 -41.25 curveto +69 -41.25 69 -55 69 -55 curveto +69 -55 23 -55 23 -55 curveto +23 -55 23 -41.25 23 -41.25 curveto +23 -41.25 0 -41.25 0 -41.25 curveto +0 -41.25 0 -13.75 0 -13.75 curveto +cl +0.0313725 0 0 0.454902 cmyk eofill +0 -13.75 m +0 -13.75 23 -13.75 23 -13.75 curveto +23 -13.75 23 0 23 0 curveto +23 0 69 0 69 0 curveto +69 0 69 -13.75 69 -13.75 curveto +69 -13.75 92 -13.75 92 -13.75 curveto +92 -13.75 92 -41.25 92 -41.25 curveto +92 -41.25 69 -41.25 69 -41.25 curveto +69 -41.25 69 -55 69 -55 curveto +69 -55 23 -55 23 -55 curveto +23 -55 23 -41.25 23 -41.25 curveto +23 -41.25 0 -41.25 0 -41.25 curveto +0 -41.25 0 -13.75 0 -13.75 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +209 49.53 tr +gs +4.98047 1.26367 tr +0 0 0 1 cmyk (G38) ArialMT 1.2 shgf +gr +gs +13.6465 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +20.3203 1.26367 tr +0 0 0 1 cmyk (G80) ArialMT 1.2 shgf +gr +gs +30.3164 1.26367 tr +0 0 0 1 cmyk (G83) ArialMT 1.2 shgf +gr +gs +36.9902 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +43.6641 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +50.3379 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +57.0117 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +63.6855 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +170 120.53 tr +48.2961 ro +0 0 m +135.281 0 li +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +256 268.53 tr +0 0 m +0 0 31 0 31 0 curveto +31 0 31 -72.7448 31 -72.7448 curveto +31 -72.7448 0 -72.7448 0 -72.7448 curveto +0 -72.7448 0 0 0 0 curveto +cl +0.27451 0 0.0901961 0.454902 cmyk eofill +0 0 m +0 0 31 0 31 0 curveto +31 0 31 -72.7448 31 -72.7448 curveto +31 -72.7448 0 -72.7448 0 -72.7448 curveto +0 -72.7448 0 0 0 0 curveto +cl +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +262 209.53 tr +90 ro +gs +7.32715 1.41016 tr +0 0 0 1 cmyk (G79) ArialMT 1.2 shgf +gr +gs +9.99316 1.41016 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +12.6592 1.41016 tr +0 0 0 1 cmyk (G69) ArialMT 1.2 shgf +gr +gs +19.333 1.41016 tr +0 0 0 1 cmyk (G78) ArialMT 1.2 shgf +gr +gs +25.333 1.41016 tr +0 0 0 1 cmyk (G70) ArialMT 1.2 shgf +gr +gs +31.333 1.41016 tr +0 0 0 1 cmyk (G68) ArialMT 1.2 shgf +gr +gs +38.0068 1.41016 tr +0 0 0 1 cmyk (G79) ArialMT 1.2 shgf +gr +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +249 71.53 tr +77.8285 ro +0 0 m +52.1728 0 li +0 0 0 1 cmyk st +gr +gs +1 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +433 167.53 tr +gs +1 0.447754 tr +0 0 0 1 cmyk (G18) ArialMT 0.9 shgf +gr +gs +1 -10.3522 tr +0 0 0 1 cmyk (G18) ArialMT 0.9 shgf +gr +gs +3.50049 -10.3522 tr +0 0 0 1 cmyk (G85) ArialMT 0.9 shgf +gr +gs +6.49756 -10.3522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +11.5029 -10.3522 tr +0 0 0 1 cmyk (G86) ArialMT 0.9 shgf +gr +gs +16.0029 -10.3522 tr +0 0 0 1 cmyk (G82) ArialMT 0.9 shgf +gr +gs +21.0083 -10.3522 tr +0 0 0 1 cmyk (G88) ArialMT 0.9 shgf +gr +gs +26.0137 -10.3522 tr +0 0 0 1 cmyk (G85) ArialMT 0.9 shgf +gr +gs +29.0107 -10.3522 tr +0 0 0 1 cmyk (G70) ArialMT 0.9 shgf +gr +gs +33.5107 -10.3522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +38.5161 -10.3522 tr +0 0 0 1 cmyk (G20) ArialMT 0.9 shgf +gr +gs +43.5215 -10.3522 tr +0 0 0 1 cmyk (G18) ArialMT 0.9 shgf +gr +gs +46.022 -10.3522 tr +0 0 0 1 cmyk (G70) ArialMT 0.9 shgf +gr +gs +50.522 -10.3522 tr +0 0 0 1 cmyk (G82) ArialMT 0.9 shgf +gr +gs +55.5273 -10.3522 tr +0 0 0 1 cmyk (G81) ArialMT 0.9 shgf +gr +gs +60.5327 -10.3522 tr +0 0 0 1 cmyk (G87) ArialMT 0.9 shgf +gr +gs +63.0332 -10.3522 tr +0 0 0 1 cmyk (G68) ArialMT 0.9 shgf +gr +gs +68.0386 -10.3522 tr +0 0 0 1 cmyk (G70) ArialMT 0.9 shgf +gr +gs +72.5386 -10.3522 tr +0 0 0 1 cmyk (G87) ArialMT 0.9 shgf +gr +gs +75.0391 -10.3522 tr +0 0 0 1 cmyk (G86) ArialMT 0.9 shgf +gr +gs +1 -21.1522 tr +0 0 0 1 cmyk (G18) ArialMT 0.9 shgf +gr +gs +3.50049 -21.1522 tr +0 0 0 1 cmyk (G85) ArialMT 0.9 shgf +gr +gs +6.49756 -21.1522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +11.5029 -21.1522 tr +0 0 0 1 cmyk (G86) ArialMT 0.9 shgf +gr +gs +16.0029 -21.1522 tr +0 0 0 1 cmyk (G82) ArialMT 0.9 shgf +gr +gs +21.0083 -21.1522 tr +0 0 0 1 cmyk (G88) ArialMT 0.9 shgf +gr +gs +26.0137 -21.1522 tr +0 0 0 1 cmyk (G85) ArialMT 0.9 shgf +gr +gs +29.0107 -21.1522 tr +0 0 0 1 cmyk (G70) ArialMT 0.9 shgf +gr +gs +33.5107 -21.1522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +38.5161 -21.1522 tr +0 0 0 1 cmyk (G20) ArialMT 0.9 shgf +gr +gs +43.5215 -21.1522 tr +0 0 0 1 cmyk (G18) ArialMT 0.9 shgf +gr +gs +46.022 -21.1522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +51.0273 -21.1522 tr +0 0 0 1 cmyk (G89) ArialMT 0.9 shgf +gr +gs +55.5273 -21.1522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +60.5327 -21.1522 tr +0 0 0 1 cmyk (G81) ArialMT 0.9 shgf +gr +gs +65.5381 -21.1522 tr +0 0 0 1 cmyk (G87) ArialMT 0.9 shgf +gr +gs +68.0386 -21.1522 tr +0 0 0 1 cmyk (G86) ArialMT 0.9 shgf +gr +gs +1 -31.9522 tr +0 0 0 1 cmyk (G18) ArialMT 0.9 shgf +gr +gs +3.50049 -31.9522 tr +0 0 0 1 cmyk (G85) ArialMT 0.9 shgf +gr +gs +6.49756 -31.9522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +11.5029 -31.9522 tr +0 0 0 1 cmyk (G86) ArialMT 0.9 shgf +gr +gs +16.0029 -31.9522 tr +0 0 0 1 cmyk (G82) ArialMT 0.9 shgf +gr +gs +21.0083 -31.9522 tr +0 0 0 1 cmyk (G88) ArialMT 0.9 shgf +gr +gs +26.0137 -31.9522 tr +0 0 0 1 cmyk (G85) ArialMT 0.9 shgf +gr +gs +29.0107 -31.9522 tr +0 0 0 1 cmyk (G70) ArialMT 0.9 shgf +gr +gs +33.5107 -31.9522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +38.5161 -31.9522 tr +0 0 0 1 cmyk (G21) ArialMT 0.9 shgf +gr +gs +1 -42.7522 tr +0 0 0 1 cmyk (G18) ArialMT 0.9 shgf +gr +gs +3.50049 -42.7522 tr +0 0 0 1 cmyk (G86) ArialMT 0.9 shgf +gr +gs +8.00049 -42.7522 tr +0 0 0 1 cmyk (G72) ArialMT 0.9 shgf +gr +gs +13.0059 -42.7522 tr +0 0 0 1 cmyk (G68) ArialMT 0.9 shgf +gr +gs +18.0112 -42.7522 tr +0 0 0 1 cmyk (G85) ArialMT 0.9 shgf +gr +gs +21.0083 -42.7522 tr +0 0 0 1 cmyk (G70) ArialMT 0.9 shgf +gr +gs +25.5083 -42.7522 tr +0 0 0 1 cmyk (G75) ArialMT 0.9 shgf +gr +gr +gs +37.481 396.049 m +37.481 396.049 193.443 396.049 193.443 396.049 curveto +193.443 396.049 193.443 353.011 193.443 353.011 curveto +193.443 353.011 37.481 353.011 37.481 353.011 curveto +37.481 353.011 37.481 396.049 37.481 396.049 curveto +cl +eoclip newpath +gs +1.03793 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +38 395.53 tr +154.924 -21 m +154.924 -32.5984 120.245 -42 77.4621 -42 curveto +34.681 -42 0 -32.5984 0 -21 curveto +0 -9.40202 34.681 0 77.4621 0 curveto +120.245 0 154.924 -9.40202 154.924 -21 curveto +cl +0.403922 0 0.137255 0.196078 cmyk eofill +154.924 -21 m +154.924 -32.5984 120.245 -42 77.4621 -42 curveto +34.681 -42 0 -32.5984 0 -21 curveto +0 -9.40202 34.681 0 77.4621 0 curveto +120.245 0 154.924 -9.40202 154.924 -21 curveto +cl +0 0 0 1 cmyk st +gr +gs +1.03793 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +61.669 382.53 tr +gs +1 1.26367 tr +0 0 0 1 cmyk (G36) ArialMT 1.2 shgf +gr +gs +9.00391 1.26367 tr +0 0 0 1 cmyk (G74) ArialMT 1.2 shgf +gr +gs +15.6777 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +22.3516 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +29.0254 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +35.6934 1.26367 tr +0 0 0 1 cmyk (G11) ArialMT 1.2 shgf +gr +gs +39.6895 1.26367 tr +0 0 0 1 cmyk (G54) ArialMT 1.2 shgf +gr +gs +47.6934 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +51.0273 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +55.0234 1.26367 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +57.6895 1.26367 tr +0 0 0 1 cmyk (G74) ArialMT 1.2 shgf +gr +gs +64.3633 1.26367 tr +0 0 0 1 cmyk (G76) ArialMT 1.2 shgf +gr +gs +70.3633 1.26367 tr +0 0 0 1 cmyk (G73) ArialMT 1.2 shgf +gr +gs +73.6973 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +80.3711 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +87.0449 1.26367 tr +0 0 0 1 cmyk (G71) ArialMT 1.2 shgf +gr +gs +93.7188 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +100.393 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +104.389 1.26367 tr +0 0 0 1 cmyk (G12) ArialMT 1.2 shgf +gr +gr +gr +gs +24.557 326.049 m +24.557 326.049 180.519 326.049 180.519 326.049 curveto +180.519 326.049 180.519 283.011 180.519 283.011 curveto +180.519 283.011 24.557 283.011 24.557 283.011 curveto +24.557 283.011 24.557 326.049 24.557 326.049 curveto +cl +eoclip newpath +gs +1.03793 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +25.076 325.53 tr +154.924 -21 m +154.924 -32.5984 120.245 -42 77.4621 -42 curveto +34.681 -42 0 -32.5984 0 -21 curveto +0 -9.40202 34.681 0 77.4621 0 curveto +120.245 0 154.924 -9.40202 154.924 -21 curveto +cl +0.403922 0 0.137255 0.196078 cmyk eofill +154.924 -21 m +154.924 -32.5984 120.245 -42 77.4621 -42 curveto +34.681 -42 0 -32.5984 0 -21 curveto +0 -9.40202 34.681 0 77.4621 0 curveto +120.245 0 154.924 -9.40202 154.924 -21 curveto +cl +0 0 0 1 cmyk st +gr +gs +1.03793 sw +0 setlinecap +0 setlinejoin +[] 0 setdash +36 313.53 tr +gs +1 1.26367 tr +0 0 0 1 cmyk (G36) ArialMT 1.2 shgf +gr +gs +9.00391 1.26367 tr +0 0 0 1 cmyk (G74) ArialMT 1.2 shgf +gr +gs +15.6777 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +22.3516 1.26367 tr +0 0 0 1 cmyk (G81) ArialMT 1.2 shgf +gr +gs +29.0254 1.26367 tr +0 0 0 1 cmyk (G87) ArialMT 1.2 shgf +gr +gs +35.6934 1.26367 tr +0 0 0 1 cmyk (G11) ArialMT 1.2 shgf +gr +gs +39.6895 1.26367 tr +0 0 0 1 cmyk (G49) ArialMT 1.2 shgf +gr +gs +48.3555 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +55.0293 1.26367 tr +0 0 0 1 cmyk (G83) ArialMT 1.2 shgf +gr +gs +61.7031 1.26367 tr +0 0 0 1 cmyk (G82) ArialMT 1.2 shgf +gr +gs +68.377 1.26367 tr +0 0 0 1 cmyk (G80) ArialMT 1.2 shgf +gr +gs +78.373 1.26367 tr +0 0 0 1 cmyk (G88) ArialMT 1.2 shgf +gr +gs +85.0469 1.26367 tr +0 0 0 1 cmyk (G78) ArialMT 1.2 shgf +gr +gs +94.3809 1.26367 tr +0 0 0 1 cmyk (G73) ArialMT 1.2 shgf +gr +gs +97.7148 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +104.389 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +111.062 1.26367 tr +0 0 0 1 cmyk (G71) ArialMT 1.2 shgf +gr +gs +117.736 1.26367 tr +0 0 0 1 cmyk (G72) ArialMT 1.2 shgf +gr +gs +124.41 1.26367 tr +0 0 0 1 cmyk (G85) ArialMT 1.2 shgf +gr +gs +128.406 1.26367 tr +0 0 0 1 cmyk (G12) ArialMT 1.2 shgf +gr +gr +gr +%%PageTrailer +restore +sp +%%Trailer +end +%%EOF diff --git a/doc/pics/concept.png b/doc/pics/concept.png new file mode 100644 index 00000000..12435f8a Binary files /dev/null and b/doc/pics/concept.png differ diff --git a/doc/pics/concept.sla b/doc/pics/concept.sla new file mode 100644 index 00000000..4d2fb510 --- /dev/null +++ b/doc/pics/concept.sla @@ -0,0 +1,783 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +